diff --git a/1 b/1 new file mode 100644 index 0000000..095d45d --- /dev/null +++ b/1 @@ -0,0 +1,79 @@ +#pragma once +#include +#include +#include + +/* 舵机编号枚举 */ +enum class EServo : uint8_t { + SERVO_0 = 0, + SERVO_1, + SERVO_2, +}; + +/* 电机运行模式 */ +enum class EMotorOperatingMode : uint8_t { + Position, // 位置模式 + Velocity, // 速度模式 + Current, // 电流 / 力矩模式 +}; + +/* 电机报警类型(可以根据 Dynamixel 详细 bitmask 扩展) */ +enum class EMotorAlarm : uint16_t { + InputVoltageError = 0x01, + OverTemperature = 0x02, + MotorEncoderError = 0x04, + Overload = 0x08, + DriverFault = 0x10, + // 更多可按需要添加 +}; + +/* 电机运动命令结构体 */ +struct ServoCommand { + EServo servo; // 哪个电机 + EMotorOperatingMode mode; // 运行模式 + int32_t target; // 目标值 + uint32_t velocity_limit = 0; // 最大速度(0 = 不修改) + uint32_t acceleration = 0; // 加速度(0 = 不修改) + uint16_t current_limit = 0; // 电流限制(0 = 不修改) +}; + +/* 电机控制接口 */ +class ServoControl +{ +public: + ServoControl(); + ~ServoControl(); + + void servoInit(EServo servo); + bool servoExecute(const ServoCommand& cmd); + + int32_t getMotorPosition(EServo servo); + int32_t getMotorVelocity(EServo servo); + int32_t getMotorCurrent(EServo servo); + + /* ====================== + 新增报警查询接口 + 返回 bitmask,0 = 无报警 + ====================== */ + uint16_t getMotorAlarmStatus(EServo servo); + bool hasMotorAlarm(EServo servo, EMotorAlarm alarm); + +private: + bool enableTorque(EServo servo); + bool disableTorque(EServo servo); + bool setOperatingMode(EServo servo, EMotorOperatingMode mode); + bool setVelocityLimit(EServo servo, uint32_t vel); + bool setAcceleration(EServo servo, uint32_t acc); + bool setCurrentLimit(EServo servo, uint16_t cur); + + struct ServoState { + EMotorOperatingMode mode; + int32_t position; + int32_t velocity; + int32_t current; + bool torque_on; + uint16_t alarm_status; // 新增:报警状态 + }; + + ServoState servo_states_[3]; +}; diff --git a/YOLO—detect/annotations.xml b/YOLO—detect/annotations.xml deleted file mode 100644 index f4fd1ff..0000000 --- a/YOLO—detect/annotations.xml +++ /dev/null @@ -1,122 +0,0 @@ - - - 1.1 - - - 258 - 12.161 - 27 - annotation - 0 - - 2025-12-16 02:07:59.378104+00:00 - 2025-12-16 02:14:18.382442+00:00 - default - 0 - 26 - - - - 176 - 0 - 26 - http://www.xj-robot.com:9000/api/jobs/176 - - - - huangxin - 2193534909@qq.com - - - - - - - - 2025-12-16 02:14:39.603909+00:00 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/angle_base_obb/1.png b/angle_base_obb/1.png deleted file mode 100644 index 0695c20..0000000 Binary files a/angle_base_obb/1.png and /dev/null differ diff --git a/angle_base_obb/11.py b/angle_base_obb/11.py new file mode 100644 index 0000000..e69de29 diff --git a/angle_base_obb/angle_caculate.py b/angle_base_obb/angle_caculate.py deleted file mode 100644 index ae541a0..0000000 --- a/angle_base_obb/angle_caculate.py +++ /dev/null @@ -1,78 +0,0 @@ -import cv2 -import os -import numpy as np -from ultralytics import YOLO - -def predict_obb_best_angle(model_path, image_path, save_path=None): - """ - 输入: - model_path: YOLO 权重路径 - image_path: 图片路径 - save_path: 可选,保存带标注图像 - 输出: - angle_deg: 置信度最高两个框的主方向夹角(度),如果检测少于两个目标返回 None - annotated_img: 可视化图像 - """ - # 1. 加载模型 - model = YOLO(model_path) - - # 2. 读取图像 - img = cv2.imread(image_path) - if img is None: - print(f"无法读取图像: {image_path}") - return None, None - - # 3. 推理 OBB - results = model(img, save=False, imgsz=640, conf=0.3, mode='obb') - result = results[0] - print(result) - - # 4. 可视化 - annotated_img = result.plot() - if save_path: - os.makedirs(os.path.dirname(save_path), exist_ok=True) - cv2.imwrite(save_path, annotated_img) - print(f"推理结果已保存至: {save_path}") - - # 5. 提取旋转角度和置信度 - boxes = result.obb - if boxes is None or len(boxes) < 2: - print("检测到少于两个目标,无法计算夹角。") - return None, annotated_img - - box_info = [] - for box in boxes: - conf = box.conf.cpu().numpy()[0] - 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 - box_info.append((conf, direction)) - - # 6. 取置信度最高两个框 - box_info = sorted(box_info, key=lambda x: x[0], reverse=True) - dir1, dir2 = box_info[0][1], box_info[1][1] - - # 7. 计算夹角(最小夹角,0~90°) - diff = abs(dir1 - dir2) - diff = min(diff, np.pi - diff) - angle_deg = np.degrees(diff) - - print(f"置信度最高两个框主方向夹角: {angle_deg:.2f}°") - return angle_deg, annotated_img - - -# ------------------- 测试 ------------------- -if __name__ == "__main__": - weight_path = r'obb.pt' - #weight_path = r'obb.pt' - image_path = r"1.png" - save_path = "./inference_results/detected_3.jpg" - - #angle_deg, annotated_img = predict_obb_best_angle(weight_path, image_path, save_path) - angle_deg,_ = predict_obb_best_angle(weight_path, image_path, save_path) - annotated_img = None - print(angle_deg) - if annotated_img is not None: - cv2.imshow("YOLO OBB Prediction", annotated_img) - cv2.waitKey(0) - cv2.destroyAllWindows() diff --git a/angle_base_obb/angle_caculate_file.py b/angle_base_obb/angle_caculate_file.py index 4d1fa65..9041f47 100644 --- a/angle_base_obb/angle_caculate_file.py +++ b/angle_base_obb/angle_caculate_file.py @@ -6,19 +6,28 @@ from ultralytics import YOLO IMG_EXTENSIONS = {'.jpg', '.jpeg', '.png', '.bmp', '.tif', '.tiff', '.webp'} -def process_obb_images(model_path, image_dir, output_dir="./inference_results", conf_thresh=0.15, imgsz=640): - """ - 批量处理图像的 OBB 推理,计算每张图像检测目标的主方向和夹角。 +def draw_direction(img, cx, cy, angle_deg, length=80, color=(0, 255, 0)): + """画主方向箭头""" + theta = np.radians(angle_deg) + x2 = int(cx + length * np.cos(theta)) + y2 = int(cy + length * np.sin(theta)) + cv2.arrowedLine( + img, + (int(cx), int(cy)), + (x2, y2), + color, + 2, + tipLength=0.2 + ) - 输入: - model_path: YOLO 权重路径 - image_dir: 图像文件夹路径 - output_dir: 输出结果保存路径 - conf_thresh: 置信度阈值 - imgsz: 输入图像大小 - 输出: - results_dict: {image_filename: {'angles_deg': [...], 'pairwise_angles_deg': [...]}} - """ + +def process_obb_images( + model_path, + image_dir, + output_dir="./inference_results", + conf_thresh=0.15, + imgsz=640 +): os.makedirs(output_dir, exist_ok=True) results_dict = {} @@ -26,8 +35,11 @@ def process_obb_images(model_path, image_dir, output_dir="./inference_results", 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] + 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 results_dict @@ -40,63 +52,101 @@ def process_obb_images(model_path, image_dir, output_dir="./inference_results", img = cv2.imread(img_path) if img is None: - print(f"❌ 跳过:无法读取图像 {img_path}") + print("❌ 读取失败,跳过") continue - # 推理 OBB - results = model(img, save=False, imgsz=imgsz, conf=conf_thresh, mode='obb') + # ---------- OBB 推理 ---------- + results = model(img, save=False, imgsz=imgsz, conf=conf_thresh, mode="obb") result = results[0] annotated_img = result.plot() - # 保存可视化 - save_path = os.path.join(output_dir, "detected_" + img_filename) - cv2.imwrite(save_path, annotated_img) - print(f"✅ 推理结果已保存至: {save_path}") - - # 提取旋转角 boxes = result.obb angles_deg = [] + centers = [] + if boxes is None or len(boxes) == 0: - print("❌ 该图像中未检测到任何目标") + print("❌ 未检测到目标") else: for i, box in enumerate(boxes): - cls = int(box.cls.cpu().numpy()[0]) - conf = box.conf.cpu().numpy()[0] 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) - print(f" Box {i + 1}: Class={cls}, Conf={conf:.3f}, 主方向={angle_deg:.2f}°") - # 两两夹角 + angles_deg.append(angle_deg) + centers.append((int(cx), int(cy))) + + print(f" Box {i + 1} 主方向: {angle_deg:.2f} deg") + + # 主方向可视化 + draw_direction(annotated_img, cx, cy, angle_deg) + cv2.circle(annotated_img, (int(cx), int(cy)), 4, (0, 0, 255), -1) + + # ---------- 两两夹角 ---------- 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])) + diff_rad = abs( + np.radians(angles_deg[i]) - + np.radians(angles_deg[j]) + ) min_diff_rad = min(diff_rad, np.pi - diff_rad) - pairwise_angles_deg.append(np.degrees(min_diff_rad)) - print(f" Box {i + 1} 与 Box {j + 1} 夹角: {np.degrees(min_diff_rad):.2f}°") + angle_ij = np.degrees(min_diff_rad) + + pairwise_angles_deg.append(angle_ij) + print( + f" Box {i + 1} 与 Box {j + 1} 夹角: {angle_ij:.2f} deg" + ) + + # ---------- 右上角粗体 angle ---------- + if pairwise_angles_deg: + max_angle = max(pairwise_angles_deg) + h, w = annotated_img.shape[:2] + + text = f"angle: {max_angle:.1f} deg" + + # 粗体效果(多次叠加) + for dx, dy in [(0, 0), (1, 0), (0, 1), (1, 1)]: + cv2.putText( + annotated_img, + text, + (w - 300 + dx, 40 + dy), + cv2.FONT_HERSHEY_SIMPLEX, + 1.2, + (0, 0, 255), + 3 + ) + + # ---------- 保存 ---------- + save_path = os.path.join(output_dir, "detected_" + img_filename) + cv2.imwrite(save_path, annotated_img) + print(f"✅ 保存完成: {save_path}") - # 保存每张图像结果 results_dict[img_filename] = { "angles_deg": angles_deg, "pairwise_angles_deg": pairwise_angles_deg } - print("\n所有图像处理完成!") + print("\n🎉 全部处理完成") return results_dict -# ------------------- 测试调用 ------------------- +# ------------------- 主入口 ------------------- if __name__ == "__main__": - MODEL_PATH = r'/home/hx/yolo/ultralytics_yolo11-main/runs/train/exp_obb_new3/weights/best.pt' - IMAGE_SOURCE_DIR = r"/media/hx/04e879fa-d697-4b02-ac7e-a4148876ebb0/dataset/obb/val" + MODEL_PATH = r"/home/hx/yolo/ultralytics_yolo11-main/runs/train/exp_obb_new3/weights/best.pt" + IMAGE_SOURCE_DIR = r"/home/hx/yolo/angle_base_obb/test_image" OUTPUT_DIR = "./inference_results" - results = process_obb_images(MODEL_PATH, IMAGE_SOURCE_DIR, OUTPUT_DIR) + results = process_obb_images( + MODEL_PATH, + IMAGE_SOURCE_DIR, + OUTPUT_DIR + ) + for img_name, info in results.items(): - print(f"\n {img_name}:") - print(f"主方向角度列表: {info['angles_deg']}") - print(f"两两夹角列表: {info['pairwise_angles_deg']}") + print(f"\n{img_name}") + print("主方向角:", info["angles_deg"]) + print("两两夹角:", info["pairwise_angles_deg"]) diff --git a/angle_base_obb/obb.pt b/angle_base_obb/obb.pt deleted file mode 100644 index c247a65..0000000 Binary files a/angle_base_obb/obb.pt and /dev/null differ diff --git a/angle_base_obb/test_image/1.jpg b/angle_base_obb/test_image/1.jpg deleted file mode 100644 index 2882cd5..0000000 Binary files a/angle_base_obb/test_image/1.jpg and /dev/null differ diff --git a/angle_base_obb/test_image/1.png b/angle_base_obb/test_image/1.png new file mode 100644 index 0000000..33329bc Binary files /dev/null and b/angle_base_obb/test_image/1.png differ diff --git a/angle_base_obb/test_image/2.jpg b/angle_base_obb/test_image/2.jpg deleted file mode 100644 index dcd5369..0000000 Binary files a/angle_base_obb/test_image/2.jpg and /dev/null differ diff --git a/angle_base_obb/test_image/2.png b/angle_base_obb/test_image/2.png new file mode 100644 index 0000000..dbd5c85 Binary files /dev/null and b/angle_base_obb/test_image/2.png differ diff --git a/angle_base_obb/test_image/3.jpg b/angle_base_obb/test_image/3.jpg deleted file mode 100644 index 8df7feb..0000000 Binary files a/angle_base_obb/test_image/3.jpg and /dev/null differ diff --git a/angle_base_obb/test_image/4.jpg b/angle_base_obb/test_image/4.jpg deleted file mode 100755 index d788c6d..0000000 Binary files a/angle_base_obb/test_image/4.jpg and /dev/null differ diff --git a/angle_base_obb/test_image/5.jpg b/angle_base_obb/test_image/5.jpg deleted file mode 100755 index 2b221ba..0000000 Binary files a/angle_base_obb/test_image/5.jpg and /dev/null differ diff --git a/angle_base_obb/test_image/6.jpg b/angle_base_obb/test_image/6.jpg deleted file mode 100755 index 89faa9b..0000000 Binary files a/angle_base_obb/test_image/6.jpg and /dev/null differ diff --git a/charge_3cls/main.py b/charge_3cls/main.py new file mode 100644 index 0000000..a25eace --- /dev/null +++ b/charge_3cls/main.py @@ -0,0 +1,131 @@ +import os +from pathlib import Path +import cv2 +import shutil +from ultralytics import YOLO + +# --------------------------- +# 三分类类别定义(必须与模型训练时的顺序一致!) +# --------------------------- +CLASS_NAMES = { + 0: "模具车 1", + 1: "模具车 2", + 2: "有遮挡" +} + + +# --------------------------- +# 单张图片推理函数(直接输入原图) +# --------------------------- +def classify_full_image(image_numpy, model): + """ + 直接将整张原图送入模型推理 + 输入:numpy 数组 (BGR) + 输出:(类别名称,置信度) + """ + # YOLO classification 模型会自动将输入图像 resize 到训练时的大小 (如 224x224 或 640x640) + results = model(image_numpy) + + # 获取概率分布 + 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})") + return class_name, confidence + + +# --------------------------- +# 批量推理主函数 (直接推理原图并移动) +# --------------------------- +def batch_classify_full_images(model_path, input_folder, output_root): + print(f"🚀 开始加载模型:{model_path}") + 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 + print(f"✅ 准备输出目录:{d}") + + input_folder = Path(input_folder) + image_extensions = {'.jpg', '.jpeg', '.png', '.bmp', '.tif', '.tiff'} + processed_count = 0 + error_count = 0 + + # 获取所有图片文件 + image_files = [f for f in input_folder.iterdir() if f.suffix.lower() in image_extensions] + print(f"\n📂 发现 {len(image_files)} 张图片,开始全图推理...\n") + + for img_path in image_files: + try: + print(f"📄 处理:{img_path.name}") + + # 1. 读取原图 + img = cv2.imread(str(img_path)) + if img is None: + print(f"❌ 无法读取图像 (可能是损坏或格式不支持): {img_path.name}") + error_count += 1 + continue + + # 2. 【核心变化】直接对整张图进行推理,不再裁剪 ROI + final_class, conf = classify_full_image(img, model) + print(f" 🔍 全图识别结果:{final_class} (conf={conf:.2f})") + + # 3. 确定目标目录 + dst_dir = class_dirs[final_class] + dst_path = dst_dir / img_path.name + + # 处理文件名冲突 (如果目标文件夹已有同名文件) + if dst_path.exists(): + stem = img_path.stem + suffix = img_path.suffix + counter = 1 + while True: + new_name = f"{stem}_{counter}{suffix}" + dst_path = dst_dir / new_name + if not dst_path.exists(): + break + counter += 1 + print(f" ⚠️ 目标文件已存在,重命名为:{dst_path.name}") + + # 4. 移动【原图】到对应分类文件夹 + shutil.move(str(img_path), str(dst_path)) + print(f" ✅ 成功移动原图 -> [{final_class}]") + + processed_count += 1 + + except Exception as e: + print(f"❌ 处理失败 {img_path.name}: {e}") + import traceback + traceback.print_exc() + error_count += 1 + + print(f"\n🎉 批量处理完成!") + print(f" 成功移动:{processed_count} 张") + print(f" 失败/跳过:{error_count} 张") + + +# --------------------------- +# 主程序入口 +# --------------------------- +if __name__ == "__main__": + # 配置路径 + model_path = "/home/hx/yolo/ultralytics_yolo11-main/runs/train/cls_resize_muju/exp_cls2/weights/best.pt" + input_folder = "/media/hx/04e879fa-d697-4b02-ac7e-a4148876ebb0/dataset/1/61/浇筑满" + output_root = "/media/hx/04e879fa-d697-4b02-ac7e-a4148876ebb0/dataset/1/61" + + # 注意:不再需要 roi_file 和 target_size 参数 + + batch_classify_full_images( + model_path=model_path, + input_folder=input_folder, + output_root=output_root + ) \ No newline at end of file diff --git a/image/del_photo/change.py b/image/del_photo/change.py index ec74656..69d4005 100644 --- a/image/del_photo/change.py +++ b/image/del_photo/change.py @@ -87,4 +87,4 @@ if __name__ == "__main__": print("❌ 输入文件夹不存在!") else: process_images_in_folder(folder, output_folder) - print("✅ 所有图片处理完成!") \ No newline at end of file + print("✅ 所有图片处理完成!") diff --git a/muban/1.png b/muban/1.png new file mode 100644 index 0000000..5717a3f Binary files /dev/null and b/muban/1.png differ diff --git a/muban/caijian.py b/muban/caijian.py new file mode 100644 index 0000000..27cd8cc --- /dev/null +++ b/muban/caijian.py @@ -0,0 +1,69 @@ +import os +import cv2 + +# ========================================================= +# 配置 +# ========================================================= + +SRC_DIR = "muban_image" # 原始模板目录 +DST_DIR = "muban_image2" # 裁剪后保存目录 + +# 三个 ROI(x, y, w, h) +ROI_1 = (782, 614, 164, 128) +ROI_2 = (837, 791, 100, 99) +ROI_3 = (873, 736, 141, 110) + + +# ========================================================= +# 裁剪函数 +# ========================================================= +def crop_and_save(img_path, save_dir): + img = cv2.imread(img_path) + if img is None: + print(f"[WARN] 读取失败: {img_path}") + return + + h_img, w_img = img.shape[:2] + base = os.path.splitext(os.path.basename(img_path))[0] + + roi_list = [ROI_1, ROI_2, ROI_3] + + for idx, roi in enumerate(roi_list, start=1): + x, y, w, h = roi + + # 边界保护 + x1 = max(0, x) + y1 = max(0, y) + x2 = min(w_img, x + w) + y2 = min(h_img, y + h) + + if x2 <= x1 or y2 <= y1: + print(f"[WARN] ROI_{idx} 超出图像范围: {img_path}") + continue + + roi_img = img[y1:y2, x1:x2] + + save_name = f"{base}_roi{idx}.png" + save_path = os.path.join(save_dir, save_name) + cv2.imwrite(save_path, roi_img) + + print(f"[OK] 保存: {save_path}") + + +# ========================================================= +# main +# ========================================================= +if __name__ == "__main__": + if not os.path.isdir(SRC_DIR): + raise RuntimeError(f"源目录不存在: {SRC_DIR}") + + os.makedirs(DST_DIR, exist_ok=True) + + for fname in os.listdir(SRC_DIR): + if not fname.lower().endswith((".png", ".jpg", ".jpeg", ".bmp")): + continue + + img_path = os.path.join(SRC_DIR, fname) + crop_and_save(img_path, DST_DIR) + + print("\n[INFO] 所有图片裁剪完成") diff --git a/muban/choose-roi.py b/muban/choose-roi.py new file mode 100644 index 0000000..60c01b7 --- /dev/null +++ b/muban/choose-roi.py @@ -0,0 +1,46 @@ +import cv2 +import os + +def select_and_output_roi(img_path, save=True, save_dir="roi_output"): + img = cv2.imread(img_path) + if img is None: + raise RuntimeError("无法读取图片") + + # OpenCV 自带 ROI 选择工具 + roi = cv2.selectROI( + windowName="Select ROI (ENTER or C to confirm, ESC to quit)", + img=img, + showCrosshair=True, + fromCenter=False + ) + + cv2.destroyAllWindows() + + x, y, w, h = roi + print(f"[INFO] ROI = (x={x}, y={y}, w={w}, h={h})") + + roi_img = img[y:y+h, x:x+w] + + if save: + os.makedirs(save_dir, exist_ok=True) + save_path = os.path.join(save_dir, "roi.png") + cv2.imwrite(save_path, roi_img) + print(f"[INFO] ROI 图像已保存: {save_path}") + + return roi, roi_img + + +# ---------------------------- +# main 测试 +# ---------------------------- +if __name__ == "__main__": + IMAGE_PATH = "test2.png" # 换成你的图片 + + roi, roi_img = select_and_output_roi( + IMAGE_PATH, + save=True + ) + + cv2.imshow("ROI Result", roi_img) + cv2.waitKey(0) + cv2.destroyAllWindows() diff --git a/muban/main.py b/muban/main.py new file mode 100644 index 0000000..dfb688b --- /dev/null +++ b/muban/main.py @@ -0,0 +1,165 @@ +import os +import cv2 +import numpy as np + +# ========================================================= +# 全局配置 +# ========================================================= + +TEMPLATE_DIR = "./muban_image1" + +# 三个 ROI(x, y, w, h) +ROI_1 = (782, 614, 164, 128) +ROI_2 = (837, 791, 100, 99) +ROI_3 = (873, 736, 141, 110) + +USE_GRAY = False +TM_METHOD = cv2.TM_CCOEFF_NORMED +SCORE_THRESH = 0.4 + + +# ========================================================= +# 模板加载 +# ========================================================= +def load_templates(template_dir): + templates = [] + if not os.path.isdir(template_dir): + raise RuntimeError(f"模板目录不存在: {template_dir}") + + for fname in os.listdir(template_dir): + if not fname.lower().endswith((".png", ".jpg", ".jpeg", ".bmp")): + continue + + path = os.path.join(template_dir, fname) + img = cv2.imread(path) + if img is None: + continue + + if USE_GRAY: + img = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) + + h, w = img.shape[:2] + name = os.path.splitext(fname)[0] + + templates.append({ + "name": name, + "img": img, + "h": h, + "w": w + }) + + print(f"[INFO] 已加载 {len(templates)} 个模板") + return templates + + +TEMPLATES = load_templates(TEMPLATE_DIR) + + +# ========================================================= +# ROI 裁剪 +# ========================================================= +def crop_roi(img, roi): + x, y, w, h = roi + return img[y:y + h, x:x + w] + + +# ========================================================= +# 单 ROI 模板匹配 +# ========================================================= +def match_one_roi(img, roi): + roi_img = crop_roi(img, roi) + if roi_img.size == 0: + return None, 0.0 + + if USE_GRAY: + roi_img = cv2.cvtColor(roi_img, cv2.COLOR_BGR2GRAY) + + best_name = None + best_score = -1.0 + + for t in TEMPLATES: + if t["h"] > roi_img.shape[0] or t["w"] > roi_img.shape[1]: + continue + + res = cv2.matchTemplate(roi_img, t["img"], TM_METHOD) + _, max_val, _, _ = cv2.minMaxLoc(res) + + if max_val > best_score: + best_score = max_val + best_name = t["name"] + + if best_score < SCORE_THRESH: + return None, best_score + + return best_name, best_score + + +# ========================================================= +# 核心逻辑:ROI_1 → ROI_2 → ROI_3 +# ========================================================= +def match_image(img): + roi_list = [ + ("roi1", ROI_1), + ("roi2", ROI_2), + ("roi3", ROI_3), + ] + + for roi_name, roi in roi_list: + name, score = match_one_roi(img, roi) + if name is not None: + return { + "result": name, + "roi": roi_name, + "score": score + } + + return { + "result": None, + "roi": None, + "score": 0.0 + } + + +# ========================================================= +# 可视化(只画命中的 ROI) +# ========================================================= +def draw_result(img, result): + vis = img.copy() + + roi_map = { + "roi1": ROI_1, + "roi2": ROI_2, + "roi3": ROI_3 + } + + if result["roi"] not in roi_map: + return vis + + x, y, w, h = roi_map[result["roi"]] + text = f'{result["result"]} {result["score"]:.2f}' + + cv2.rectangle(vis, (x, y), (x + w, y + h), (0, 255, 0), 2) + cv2.putText( + vis, text, (x, y - 10), + cv2.FONT_HERSHEY_SIMPLEX, + 0.7, (0, 255, 0), 2 + ) + + return vis + + +# ========================================================= +# main 测试 +# ========================================================= +if __name__ == "__main__": + img = cv2.imread("1.png") + if img is None: + raise RuntimeError("测试图片未找到") + + result = match_image(img) + print("[RESULT]", result) + + vis = draw_result(img, result) + cv2.imshow("template match", vis) + cv2.waitKey(0) + cv2.destroyAllWindows() diff --git a/muju_cls/5.png b/muju_cls/5.png new file mode 100644 index 0000000..84415fc Binary files /dev/null and b/muju_cls/5.png differ diff --git a/muju_cls/5divid.py b/muju_cls/5divid.py new file mode 100644 index 0000000..4841935 --- /dev/null +++ b/muju_cls/5divid.py @@ -0,0 +1,70 @@ +import os +import shutil +from pathlib import Path + +def split_images_into_five(source_dir, target_root, move_instead_of_copy=True): + """ + 将 source_dir 中的图片均分为 5 份,保存到 target_root/1 ~ 5/ + + :param source_dir: 源图片文件夹路径 + :param target_root: 目标根目录(会创建 1~5 子文件夹) + :param move_instead_of_copy: True 表示移动,False 表示复制(默认为 True 移动) + """ + source = Path(source_dir) + target_root = Path(target_root) + + # 支持的图片扩展名 + image_extensions = {'.jpg', '.jpeg', '.png', '.bmp', '.tiff', '.tif', '.webp'} + + # 获取所有图片文件(按名称排序,保证可重复性) + all_images = sorted([ + f for f in source.iterdir() + if f.is_file() and f.suffix.lower() in image_extensions + ]) + + if not all_images: + print("❌ 源文件夹中没有找到图片!") + return + + total = len(all_images) + print(f"📁 共找到 {total} 张图片") + + # 均分为 5 份 + chunk_size = (total + 4) // 5 # 向上取整,确保覆盖所有图片 + + # 创建 1~5 文件夹并分发图片 + for i in range(5): + start_idx = i * chunk_size + end_idx = min((i + 1) * chunk_size, total) + if start_idx >= total: + break # 防止空分片 + + folder_name = str(i + 1) + target_folder = target_root / folder_name + target_folder.mkdir(parents=True, exist_ok=True) + + chunk_files = all_images[start_idx:end_idx] + print(f"📦 文件夹 {folder_name}: 分配 {len(chunk_files)} 张图片") + + for img_path in chunk_files: + dst = target_folder / img_path.name + if move_instead_of_copy: + shutil.move(str(img_path), str(dst)) + print(f" 📂 已移动: {img_path.name}") + else: + shutil.copy2(str(img_path), str(dst)) + print(f" 📂 已复制: {img_path.name}") + + print(f"\n✅ 分割完成!结果保存在: {target_root}") + +if __name__ == "__main__": + # ======================== + # ⚙️ 配置你的路径 + # ======================== + SOURCE_DIR = "/media/hx/04e879fa-d697-4b02-ac7e-a4148876ebb0/dataset/1/分割/class3" + TARGET_ROOT = "/media/hx/04e879fa-d697-4b02-ac7e-a4148876ebb0/dataset/1/分割/class3" + + # 设置为 True 则移动图片(原图会被移走),False 则复制(推荐先用 False 测试) + MOVE_MODE = True + + split_images_into_five(SOURCE_DIR, TARGET_ROOT, move_instead_of_copy=MOVE_MODE) \ No newline at end of file diff --git a/muju_cls/chose_ROI.py b/muju_cls/chose_ROI.py new file mode 100644 index 0000000..401b2ee --- /dev/null +++ b/muju_cls/chose_ROI.py @@ -0,0 +1,93 @@ +import cv2 +import numpy as np +import os + +# 全局变量 +drawing = False # 是否正在绘制 +ix, iy = -1, -1 # 起始点 +roi_list = [] # 存储多个 ROI 坐标 [(x, y, w, h), ...] +image_path = "5.png" # <<< 修改为你自己的图像路径 +save_dir = "./ROI/muju_rois.txt" # 保存坐标的目录 + +# 创建保存目录 +os.makedirs(save_dir, exist_ok=True) + +def draw_rectangle(event, x, y, flags, param): + global ix, iy, drawing, img_copy, roi_list + + if event == cv2.EVENT_LBUTTONDOWN: + drawing = True + ix, iy = x, y + + elif event == cv2.EVENT_MOUSEMOVE: + if drawing: + # 每次移动都恢复原始图像,重新画矩形 + img_copy = img.copy() + cv2.rectangle(img_copy, (ix, iy), (x, y), (0, 255, 0), 2) + cv2.imshow("Select ROI", img_copy) + + elif event == cv2.EVENT_LBUTTONUP: + drawing = False + w = x - ix + h = y - iy + if w != 0 and h != 0: + # 确保宽高为正 + x_start = min(ix, x) + y_start = min(iy, y) + w = abs(w) + h = abs(h) + cv2.rectangle(img_copy, (x_start, y_start), (x_start + w, y_start + h), (0, 255, 0), 2) + cv2.imshow("Select ROI", img_copy) + # 添加到列表 + roi_list.append((x_start, y_start, w, h)) + print(f"已选择 ROI: (x={x_start}, y={y_start}, w={w}, h={h})") + +# 保存坐标到 .txt 文件的函数 +def save_rois_to_txt(rois, filepath): + with open(filepath, 'w') as file: + for roi in rois: + # 将每个 ROI 转换为字符串并写入文件,每行一个 ROI + line = ','.join(map(str, roi)) + '\n' + file.write(line) + print(f"💾 ROI 坐标已保存至: {filepath}") + +def select_roi(image_path): + global img, img_copy + + img = cv2.imread(image_path) + if img is None: + print(f"❌ 无法读取图像: {image_path}") + return + + img_copy = img.copy() + cv2.namedWindow("Select ROI") + cv2.setMouseCallback("Select ROI", draw_rectangle) + + print("📌 使用鼠标左键拖拽选择 ROI") + print("✅ 选择完成后按 's' 键保存坐标") + print("⏭️ 按 'n' 键跳过/下一步(可自定义)") + print("🚪 按 'q' 键退出") + + while True: + cv2.imshow("Select ROI", img_copy) + key = cv2.waitKey(1) & 0xFF + + 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}_rois1.txt") # 修改了扩展名为 .txt + save_rois_to_txt(roi_list, save_path) # 使用新的保存函数 + + elif key == ord('n'): + print("⏭️ 跳到下一张图片(此处可扩展)") + break + + elif key == ord('q'): + print("👋 退出程序") + cv2.destroyAllWindows() + return + + cv2.destroyAllWindows() + +if __name__ == "__main__": + select_roi(image_path) \ No newline at end of file diff --git a/muju_cls/main_pc.py b/muju_cls/main_pc.py new file mode 100644 index 0000000..7747740 --- /dev/null +++ b/muju_cls/main_pc.py @@ -0,0 +1,153 @@ +import os +from pathlib import Path +import cv2 +import numpy as np +import shutil +from ultralytics import YOLO + +# --------------------------- +# 三分类类别定义(必须与模型训练时的顺序一致!) +# --------------------------- +CLASS_NAMES = { + 0: "模具车1", + 1: "模具车2", + 2: "有遮挡" +} + +# --------------------------- +# 加载 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 + +# --------------------------- +# 单张图片推理函数(3分类) +# --------------------------- +def classify_image(image, model): + 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})") + return class_name, confidence + +# --------------------------- +# 批量推理主函数(移动原图) +# --------------------------- +def batch_classify_images(model_path, input_folder, output_root, roi_file, target_size=640): + + # 加载模型 + 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 + + # 加载 ROI + rois = load_global_rois(roi_file) + if not rois: + print("❌ 没有有效 ROI,退出程序") + return + + # 定义严重性等级(数值越小越“正常”,用于取最严重结果) + # 根据你的业务调整:例如“有遮挡”最严重 + severity_rank = { + "模具车1": 0, + "模具车2": 1, + "有遮挡": 2 + } + + 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)) + print(f"图像尺寸: {img.shape[1]} x {img.shape[0]}") + if img is None: + print(f"❌ 无法读取图像: {img_path.name}") + continue + + crops = crop_and_resize(img, rois, target_size) + if not crops: + print(f"⚠️ 无有效 ROI 裁剪区域: {img_path.name}") + continue + + detected_classes = [] + for roi_img, roi_idx in crops: + final_class, conf = classify_image(roi_img, model) + detected_classes.append(final_class) + print(f" 🔍 ROI{roi_idx}: {final_class} (conf={conf:.2f})") + + # 选择最严重的类别(severity_rank 值最大者) + most_severe_class = max(detected_classes, key=lambda x: severity_rank.get(x, -1)) + + # 移动原图(不是裁剪图!) + dst_path = class_dirs[most_severe_class] / img_path.name + shutil.move(str(img_path), str(dst_path)) + 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_muju/exp_cls2/weights/best.pt" + input_folder = "/media/hx/04e879fa-d697-4b02-ac7e-a4148876ebb0/dataset/1/61/浇筑满" + output_root = "/media/hx/04e879fa-d697-4b02-ac7e-a4148876ebb0/dataset/1/61" + roi_file = "/home/hx/yolo/muju_cls/roi_coordinates/muju_roi.txt" + target_size = 640 + + batch_classify_images( + model_path=model_path, + input_folder=input_folder, + output_root=output_root, + roi_file=roi_file, + target_size=target_size + ) \ No newline at end of file diff --git a/muju_cls/muju_cls_rknn.py b/muju_cls/muju_cls_rknn.py new file mode 100644 index 0000000..812c26f --- /dev/null +++ b/muju_cls/muju_cls_rknn.py @@ -0,0 +1,282 @@ +import os +import cv2 +import numpy as np +from rknnlite.api import RKNNLite + +from collections import deque + +class StableClassJudge: + """ + 连续三帧稳定判决器: + - class0 / class1 连续 3 帧 -> 输出 + - class2 -> 清空计数,重新统计 + """ + + def __init__(self, stable_frames=3, ignore_class=2): + self.stable_frames = stable_frames + self.ignore_class = ignore_class + self.buffer = deque(maxlen=stable_frames) + + def reset(self): + self.buffer.clear() + + def update(self, class_id): + """ + 输入单帧分类结果 + 返回: + - None:尚未稳定 + - class_id:稳定输出结果 + """ + + # 遇到 class2,直接清空重新计数 + if class_id == self.ignore_class: + self.reset() + return None + + self.buffer.append(class_id) + + # 缓冲未满 + if len(self.buffer) < self.stable_frames: + return None + + # 三帧完全一致 + if len(set(self.buffer)) == 1: + stable_class = self.buffer[0] + self.reset() # 输出一次后重新计数(防止重复触发) + return stable_class + + return None + +# --------------------------- +# 三分类映射 +# --------------------------- +CLASS_NAMES = { + 0: "模具车1", + 1: "模具车2", + 2: "有遮挡" +} + +# --------------------------- +# RKNN 全局实例(只加载一次) +# --------------------------- +_global_rknn = None + + +def init_rknn_model(model_path): + global _global_rknn + if _global_rknn is not None: + return _global_rknn + + rknn = RKNNLite(verbose=False) + ret = rknn.load_rknn(model_path) + if ret != 0: + raise RuntimeError(f"Load RKNN failed: {ret}") + + ret = rknn.init_runtime(core_mask=RKNNLite.NPU_CORE_0) + if ret != 0: + raise RuntimeError(f"Init runtime failed: {ret}") + + _global_rknn = rknn + print(f"[INFO] RKNN 模型加载成功: {model_path}") + return rknn + + +# --------------------------- +# 预处理 +# --------------------------- +def letterbox(image, new_size=640, color=(114, 114, 114)): + h, w = image.shape[:2] + scale = min(new_size / h, new_size / w) + nh, nw = int(h * scale), int(w * scale) + resized = cv2.resize(image, (nw, nh)) + new_img = np.full((new_size, new_size, 3), color, dtype=np.uint8) + top = (new_size - nh) // 2 + left = (new_size - nw) // 2 + new_img[top:top + nh, left:left + nw] = resized + return new_img + + +def resize_stretch(image, size=640): + return cv2.resize(image, (size, size)) + + +def preprocess_image_for_rknn( + img, + size=640, + resize_mode="stretch", + to_rgb=True, + normalize=False, + layout="NHWC" +): + if resize_mode == "letterbox": + img_box = letterbox(img, new_size=size) + else: + img_box = resize_stretch(img, size=size) + + if to_rgb: + img_box = cv2.cvtColor(img_box, cv2.COLOR_BGR2RGB) + + img_f = img_box.astype(np.float32) + + if normalize: + img_f /= 255.0 + + if layout == "NHWC": + out = np.expand_dims(img_f, axis=0) + else: + out = np.expand_dims(np.transpose(img_f, (2, 0, 1)), axis=0) + + return np.ascontiguousarray(out) + + +# --------------------------- +# 单次 RKNN 推理(三分类) +# --------------------------- +def rknn_classify_preprocessed(input_tensor, model_path): + rknn = init_rknn_model(model_path) + + outs = rknn.inference([input_tensor]) + logits = outs[0].reshape(-1).astype(np.float32) # shape = (3,) + + # softmax + exp = np.exp(logits - np.max(logits)) + probs = exp / np.sum(exp) + + class_id = int(np.argmax(probs)) + return class_id, probs + + +# --------------------------- +# ROI +# --------------------------- +def load_single_roi(txt_path): + if not os.path.exists(txt_path): + raise RuntimeError(f"ROI 文件不存在: {txt_path}") + + with open(txt_path) as f: + for line in f: + s = line.strip() + if not s: + continue + x, y, w, h = map(int, s.split(',')) + return (x, y, w, h) + + raise RuntimeError("ROI 文件为空") + + +def crop_and_return_roi(img, roi): + x, y, w, h = roi + h_img, w_img = img.shape[:2] + + if x < 0 or y < 0 or x + w > w_img or y + h > h_img: + raise RuntimeError(f"ROI 超出图像范围: {roi}") + + return img[y:y + h, x:x + w] + + +# --------------------------- +# 单张图片推理(三分类) +# --------------------------- +def classify_single_image( + frame, + model_path, + roi_file, + size=640, + resize_mode="stretch", + to_rgb=True, + normalize=False, + layout="NHWC" +): + if frame is None: + raise FileNotFoundError("❌ 输入帧为空") + + roi = load_single_roi(roi_file) + roi_img = crop_and_return_roi(frame, roi) + + input_tensor = preprocess_image_for_rknn( + roi_img, + size=size, + resize_mode=resize_mode, + to_rgb=to_rgb, + normalize=normalize, + layout=layout + ) + + class_id, probs = rknn_classify_preprocessed(input_tensor, model_path) + class_name = CLASS_NAMES.get(class_id, f"未知类别({class_id})") + + return { + "class_id": class_id, + "class": class_name, + "score": round(float(probs[class_id]), 4), + "raw": probs.tolist() + } + + + +# --------------------------- +# 示例调用 +# --------------------------- +if __name__ == "__main__": + model_path = "muju_cls.rknn" + roi_file = "./roi_coordinates/muju_roi.txt" + image_path = "./test_image/test.png" + + frame = cv2.imread(image_path) + if frame is None: + raise FileNotFoundError(f"❌ 无法读取图片: {image_path}") + + result = classify_single_image(frame, model_path, roi_file) + print("[RESULT]", result) + +# --------------------------- +# 示例判断逻辑 +''' +import cv2 +from muju_cls_rknn import classify_single_image,StableClassJudge,CLASS_NAMES + +def run_stable_classification_loop( + model_path, + roi_file, + image_source, + stable_frames=3 +): + """ + image_source: + - cv2.VideoCapture + """ + judge = StableClassJudge( + stable_frames=stable_frames, + ignore_class=2 # 有遮挡 + ) + + cap = image_source + if not hasattr(cap, "read"): + raise TypeError("image_source 必须是 cv2.VideoCapture") + + while True: + ret, frame = cap.read() + if not ret: + print("读取帧失败,退出") + break + + result = classify_single_image(frame, model_path, roi_file) + + class_id = result["class_id"] + class_name = result["class"] + score = result["score"] + + print(f"[FRAME] {class_name} conf={score}") + + stable = judge.update(class_id) + + if stable is not None: + print(f"\n稳定输出: {CLASS_NAMES[stable]} \n") + + if cv2.waitKey(1) & 0xFF == ord('q'): + break + + cap.release() + cv2.destroyAllWindows() +''' +# --------------------------- diff --git a/muju_cls/resize_dataset_image.py b/muju_cls/resize_dataset_image.py new file mode 100644 index 0000000..d49a745 --- /dev/null +++ b/muju_cls/resize_dataset_image.py @@ -0,0 +1,57 @@ +import os +import cv2 + +# ---------------------------- +# 配置 +# ---------------------------- +SOURCE_ROOT_DIR = "/media/hx/04e879fa-d697-4b02-ac7e-a4148876ebb0/dataset/1/61/1" # 原始图片根目录 +TARGET_ROOT_DIR = "/media/hx/04e879fa-d697-4b02-ac7e-a4148876ebb0/dataset/1/1" # 输出根目录 +CLASSES = ["class0","class1","class2"] # 类 +TARGET_SIZE = 640 # resize 尺 +SUBSETS = ["train", "val", "test"] + +# ---------------------------- +# 全局 ROI (x, y, w, h) +# ---------------------------- +GLOBAL_ROI = [2,880,385,200] +# ---------------------------- +# 主处理函数 +# ---------------------------- +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/muju_cls/resize—image.py b/muju_cls/resize—image.py new file mode 100644 index 0000000..f53b4fb --- /dev/null +++ b/muju_cls/resize—image.py @@ -0,0 +1,44 @@ +import os +import cv2 + +# ================== 配置 ================== +IMAGE_DIR = "/media/hx/04e879fa-d697-4b02-ac7e-a4148876ebb0/dataset/1/error" # 修改为你的图片文件夹路径 +TARGET_W = 1920 +TARGET_H = 1080 +PREFIX = "img" # 重命名前缀 +EXT = ".png" # 统一保存格式(.jpg / .png) +START_INDEX = 1 # 起始编号 +# ========================================= + +# 支持的图片后缀 +IMG_EXTS = (".jpg", ".jpeg", ".png", ".bmp", ".webp") + +# 读取并排序(保证顺序稳定) +image_files = sorted([ + f for f in os.listdir(IMAGE_DIR) + if f.lower().endswith(IMG_EXTS) +]) + +print(f"[INFO] 共找到 {len(image_files)} 张图片") + +for idx, filename in enumerate(image_files, start=START_INDEX): + img_path = os.path.join(IMAGE_DIR, filename) + + img = cv2.imread(img_path) + if img is None: + print(f"[WARN] 读取失败,跳过: {filename}") + continue + + # resize + resized = cv2.resize(img, (TARGET_W, TARGET_H), interpolation=cv2.INTER_AREA) + + # 新文件名 + new_name = f"{PREFIX}_{idx:04d}{EXT}" + new_path = os.path.join(IMAGE_DIR, new_name) + + # 保存 + cv2.imwrite(new_path, resized) + + print(f"[OK] {filename} -> {new_name}") + +print("[DONE] 全部处理完成") diff --git a/muju_cls/yolo11-cls-muju-resize.yaml b/muju_cls/yolo11-cls-muju-resize.yaml new file mode 100644 index 0000000..51ccdba --- /dev/null +++ b/muju_cls/yolo11-cls-muju-resize.yaml @@ -0,0 +1,30 @@ +# Ultralytics YOLO 🚀, AGPL-3.0 license +# YOLO11-cls image classification model. For Usage examples see https://docs.ultralytics.com/tasks/classify + +# Parameters +nc: 3 # number of classes +scales: # model compound scaling constants, i.e. 'model=yolo11n-cls.yaml' will call yolo11-cls.yaml with scale 'n' + # [depth, width, max_channels] + n: [0.50, 0.25, 1024] # summary: 151 layers, 1633584 parameters, 1633584 gradients, 3.3 GFLOPs + s: [0.50, 0.50, 1024] # summary: 151 layers, 5545488 parameters, 5545488 gradients, 12.2 GFLOPs + m: [0.50, 1.00, 512] # summary: 187 layers, 10455696 parameters, 10455696 gradients, 39.7 GFLOPs + l: [1.00, 1.00, 512] # summary: 309 layers, 12937104 parameters, 12937104 gradients, 49.9 GFLOPs + x: [1.00, 1.50, 512] # summary: 309 layers, 28458544 parameters, 28458544 gradients, 111.1 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, 2, C2PSA, [1024]] # 9 + +# YOLO11n head +head: + - [-1, 1, Classify, [nc]] # Classify diff --git a/tool/change_detect_label_cls.py b/tool/change_detect_label_cls.py new file mode 100644 index 0000000..577a8c0 --- /dev/null +++ b/tool/change_detect_label_cls.py @@ -0,0 +1,50 @@ +import os + +# ====================== +# 配置 +# ====================== +LABEL_DIR = "/media/hx/04e879fa-d697-4b02-ac7e-a4148876ebb0/dataset/detect/val" # 改成你的标签文件夹路径 +BACKUP = False # 是否备份原文件 + +def convert_labels_to_zero(label_dir): + for filename in os.listdir(label_dir): + if not filename.endswith(".txt"): + continue + + file_path = os.path.join(label_dir, filename) + + with open(file_path, "r", encoding="utf-8") as f: + lines = f.readlines() + + new_lines = [] + for line in lines: + line = line.strip() + if not line: + continue + + parts = line.split() + if len(parts) < 5: + # 非标准 yolo 行,原样保留 + new_lines.append(line) + continue + + # 强制类别改为 0 + parts[0] = "0" + new_lines.append(" ".join(parts)) + + # 备份 + if BACKUP: + backup_path = file_path + ".bak" + if not os.path.exists(backup_path): + os.rename(file_path, backup_path) + save_path = file_path + else: + save_path = file_path + + with open(save_path, "w", encoding="utf-8") as f: + f.write("\n".join(new_lines) + "\n") + + print(f"[OK] 已处理: {filename}") + +if __name__ == "__main__": + convert_labels_to_zero(LABEL_DIR) diff --git a/tool/d_n.py b/tool/d_n.py new file mode 100644 index 0000000..491c818 --- /dev/null +++ b/tool/d_n.py @@ -0,0 +1,105 @@ +import os +import shutil +import math + +# ================= 配置区域 ================= +# 【全局变量】在这里修改你要分成的份数 +N_FOLDERS = 4 + +# 源文件夹名称 (相对于当前脚本运行目录) +SOURCE_DIR_NAME = "camera02_save" + +# 支持的图片扩展名 +VALID_EXTENSIONS = ('.jpg', '.jpeg', '.png', '.bmp', '.tif', '.tiff') + + +# =========================================== + +def split_images_to_subfolders(): + # 1. 获取当前脚本所在的绝对路径 + base_dir = os.getcwd() + source_path = os.path.join(base_dir, SOURCE_DIR_NAME) + + # 2. 检查源文件夹是否存在 + if not os.path.exists(source_path): + print(f"❌ 错误: 找不到文件夹 '{source_path}'") + print("请确保脚本在包含 'camera02_save' 的目录下运行。") + return + + # 3. 获取所有图片文件列表 + all_files = os.listdir(source_path) + image_files = [ + f for f in all_files + if f.lower().endswith(VALID_EXTENSIONS) and os.path.isfile(os.path.join(source_path, f)) + ] + + # 按文件名排序,保证分配顺序一致(可选) + image_files.sort() + + total_count = len(image_files) + + if total_count == 0: + print(f"警告: '{source_path}' 中没有找到任何图片文件。") + return + + print(f"发现图片总数: {total_count}") + print(f"计划分成: {N_FOLDERS} 份") + + # 4. 计算分配逻辑 + # 使用轮询方式分配,确保每个文件夹数量尽可能平均 + # 例如:10张图分3份 -> 4, 3, 3 + + created_folders = [] + + for index, filename in enumerate(image_files): + # 计算该图片应该去哪个子文件夹 (0 到 N_FOLDERS-1) + folder_index = index % N_FOLDERS + subfolder_name = f"part_{folder_index}" + + # 构建子文件夹完整路径 + dest_folder_path = os.path.join(source_path, subfolder_name) + + # 如果文件夹不存在,则创建 + if not os.path.exists(dest_folder_path): + os.makedirs(dest_folder_path) + created_folders.append(subfolder_name) + print(f"✅ 创建子文件夹: {subfolder_name}") + + # 构建源文件和目标文件路径 + src_file = os.path.join(source_path, filename) + dst_file = os.path.join(dest_folder_path, filename) + + # 移动文件 (剪切) + try: + shutil.move(src_file, dst_file) + except Exception as e: + print(f"❌ 移动失败 {filename}: {e}") + + # 5. 统计结果 + print("\n" + "=" * 30) + print("分配完成!统计如下:") + print("=" * 30) + + # 重新扫描统计每个文件夹的数量 + final_counts = {} + for i in range(N_FOLDERS): + fname = f"part_{i}" + fpath = os.path.join(source_path, fname) + if os.path.exists(fpath): + count = len([f for f in os.listdir(fpath) if f.lower().endswith(VALID_EXTENSIONS)]) + final_counts[fname] = count + print(f"{fname}: {count} 张图片") + + print("=" * 30) + print(f"总计移动: {sum(final_counts.values())} 张图片") + + +if __name__ == "__main__": + # 提示用户确认 + print(f"当前工作目录: {os.getcwd()}") + confirm = input(f"即将把 '{SOURCE_DIR_NAME}' 中的图片均分为 {N_FOLDERS} 份。\n是否继续?(y/n): ") + + if confirm.lower() == 'y': + split_images_to_subfolders() + else: + print("操作已取消。") \ No newline at end of file diff --git a/tool/divid_val.py b/tool/divid_val.py index 82ea9a4..c9ea501 100644 --- a/tool/divid_val.py +++ b/tool/divid_val.py @@ -98,11 +98,11 @@ if __name__ == "__main__": # 修改为你自己的路径 #TRAIN_DIR = "/media/hx/04e879fa-d697-4b02-ac7e-a4148876ebb0/dataset/cls-new/19cc/train" #VAL_DIR = "/media/hx/04e879fa-d697-4b02-ac7e-a4148876ebb0/dataset/cls-new/19cc/val" - TRAIN_DIR = "/media/hx/04e879fa-d697-4b02-ac7e-a4148876ebb0/dataset/point2/train" - VAL_DIR = "/media/hx/04e879fa-d697-4b02-ac7e-a4148876ebb0/dataset/point2/val1" + TRAIN_DIR = "/media/hx/04e879fa-d697-4b02-ac7e-a4148876ebb0/dataset/1/charge/train" + VAL_DIR = "/media/hx/04e879fa-d697-4b02-ac7e-a4148876ebb0/dataset/1/charge/val" split_train_to_val( train_dir=TRAIN_DIR, val_dir=VAL_DIR, ratio=0.1, # 抽取 10% - seed=42 # 随机种子 + seed=58 # 随机种子al ) \ No newline at end of file diff --git a/ultralytics_yolo11-main/ailaidata.yaml b/ultralytics_yolo11-main/ailaidata.yaml index 37f2b27..46853a8 100644 --- a/ultralytics_yolo11-main/ailaidata.yaml +++ b/ultralytics_yolo11-main/ailaidata.yaml @@ -3,5 +3,5 @@ train: train # 数据集路径下的train.txt val: val # 数据集路径下的val.txt test: test # 数据集路径下的test.txt -nc: 1 -names: ['bag'] +nc: 2 +names: ['bag','bag35'] diff --git a/ultralytics_yolo11-main/data_seg60.yaml b/ultralytics_yolo11-main/data_seg60.yaml new file mode 100644 index 0000000..183c75a --- /dev/null +++ b/ultralytics_yolo11-main/data_seg60.yaml @@ -0,0 +1,7 @@ +path: /media/hx/04e879fa-d697-4b02-ac7e-a4148876ebb0/dataset/60seg/60-seg +train: train # 数据集路径下的train.txt +val: val # 数据集路径下的val.txt +test: test # 数据集路径下的test.txt + +nc: 1 +names: ['yemian'] diff --git a/ultralytics_yolo11-main/data_seg61.yaml b/ultralytics_yolo11-main/data_seg61.yaml new file mode 100644 index 0000000..f084766 --- /dev/null +++ b/ultralytics_yolo11-main/data_seg61.yaml @@ -0,0 +1,7 @@ +path: /media/hx/04e879fa-d697-4b02-ac7e-a4148876ebb0/dataset/61_seg1 +train: train # 数据集路径下的train.txt +val: val # 数据集路径下的val.txt +test: test # 数据集路径下的test.txt + +nc: 1 +names: ['yemian'] diff --git a/ultralytics_yolo11-main/train_ailai_detect.py b/ultralytics_yolo11-main/train_ailai_detect.py index a2b3eb3..303af05 100644 --- a/ultralytics_yolo11-main/train_ailai_detect.py +++ b/ultralytics_yolo11-main/train_ailai_detect.py @@ -1,11 +1,11 @@ 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_ailai_detect2/weights/best.pt') model = YOLO(r'/home/hx/yolo/ultralytics_yolo11-main/ultralytics/cfg/models/11/yolo11.yaml') results = model.train( data='ailaidata.yaml', - epochs=1000, + epochs=500, imgsz=640, batch=4, workers=10, @@ -14,6 +14,6 @@ if __name__ == '__main__': name='exp_ailai_detect', exist_ok=False, optimizer='AdamW', - lr0=0.0001, + lr0=0.0005, patience=0, - ) + ) \ No newline at end of file diff --git a/ultralytics_yolo11-main/train_ailai_detect_n.py b/ultralytics_yolo11-main/train_ailai_detect_n.py index 0ed89f9..094b4cd 100644 --- a/ultralytics_yolo11-main/train_ailai_detect_n.py +++ b/ultralytics_yolo11-main/train_ailai_detect_n.py @@ -26,3 +26,4 @@ results = model.train( close_mosaic=10, val=True, ) + diff --git a/ultralytics_yolo11-main/train_ailai_main.py b/ultralytics_yolo11-main/train_ailai_main.py index 38e47ff..c6e7d41 100644 --- a/ultralytics_yolo11-main/train_ailai_main.py +++ b/ultralytics_yolo11-main/train_ailai_main.py @@ -2,10 +2,11 @@ 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_ailai2/weights/best.pt') + #model = YOLO(r'/home/hx/yolo/ultralytics_yolo11-main/runs/train/exp_ailai2/weights/best.pt') + model = YOLO(r'/home/hx/yolo/ultralytics_yolo11-main/ultralytics/cfg/models/11/yolo11-ailai.yaml') results = model.train( data='data_ailai.yaml', - epochs=1000, + epochs=300, imgsz=640, batch=4, workers=10, @@ -14,6 +15,6 @@ if __name__ == '__main__': name='exp_ailai', exist_ok=False, optimizer='AdamW', - lr0=0.0001, + lr0=0.0005, patience=0, ) diff --git a/ultralytics_yolo11-main/train_cls_main.py b/ultralytics_yolo11-main/train_cls_main.py index 56d3e74..7c53129 100644 --- a/ultralytics_yolo11-main/train_cls_main.py +++ b/ultralytics_yolo11-main/train_cls_main.py @@ -1,16 +1,17 @@ from ultralytics import YOLO if __name__ == '__main__': - model = YOLO(r'/home/hx/yolo/ultralytics_yolo11-main/ultralytics/cfg/models/11/yolo11-cls-xiantiao.yaml') + model = YOLO(r'/home/hx/yolo/ultralytics_yolo11-main/ultralytics/cfg/models/11/yolo11-2cls.yaml') results = model.train( - data='/home/hx/开发/ML_xiantiao/image/datasetr1', + #data='/home/hx/开发/ML_xiantiao/image/datasetr1', + data='/media/hx/04e879fa-d697-4b02-ac7e-a4148876ebb0/dataset/1/charge', epochs=1000, imgsz=640, batch=4, workers=10, device='0', project='runs/train/cls', - name='exp_xiantiao_cls', + name='exp_zdb_cls', exist_ok=False, optimizer='AdamW', lr0=0.0005, diff --git a/ultralytics_yolo11-main/train_resize_cls_muju.py b/ultralytics_yolo11-main/train_resize_cls_muju.py new file mode 100644 index 0000000..1c0f048 --- /dev/null +++ b/ultralytics_yolo11-main/train_resize_cls_muju.py @@ -0,0 +1,19 @@ +from ultralytics import YOLO + +if __name__ == '__main__': + model = YOLO(r'/home/hx/yolo/muju_cls/yolo11-cls-muju-resize.yaml') + #model = YOLO(r'/home/hx/yolo/ultralytics_yolo11-main/runs/train/cls_resize/exp_cls6/weights/best.pt') + results = model.train( + data='/media/hx/04e879fa-d697-4b02-ac7e-a4148876ebb0/dataset/1/模具车分类resize', + epochs=500, + imgsz=640, + batch=4, + workers=10, + device='0', + project='runs/train/cls_resize_muju', + name='exp_cls', + exist_ok=False, + optimizer='AdamW', + lr0=0.0005, + patience=0, + ) diff --git a/ultralytics_yolo11-main/train_seg_resize_main.py b/ultralytics_yolo11-main/train_seg_r_main_60.py similarity index 70% rename from ultralytics_yolo11-main/train_seg_resize_main.py rename to ultralytics_yolo11-main/train_seg_r_main_60.py index 05c46a6..0cfee56 100644 --- a/ultralytics_yolo11-main/train_seg_resize_main.py +++ b/ultralytics_yolo11-main/train_seg_r_main_60.py @@ -2,20 +2,20 @@ from ultralytics import YOLO if __name__ == '__main__': # ✅ 推荐:使用官方预训练分割模型 - #model = YOLO(r'/home/hx/yolo/ultralytics_yolo11-main/runs/train/seg_j/exp2/weights/best.pt') + #model = YOLO(r'/home/hx/yolo/ultralytics_yolo11-main/runs/train/61seg/exp2/weights/best.pt') model = YOLO(r'/home/hx/yolo/ultralytics_yolo11-main/ultralytics/cfg/models/11/yolo11-seg.yaml') # 开始训练 results = model.train( - data='/home/hx/yolo/ultralytics_yolo11-main/resize_seg_data.yaml', # 数据配置文件 - epochs=100, # 训练轮数 + data='data_seg60.yaml', # 数据配置文件 + epochs=300, # 训练轮数 imgsz=1280, batch=4, # 每批图像数量 workers=10, # 数据加载线程数 device='0', # 使用 GPU 0 - project='runs/train/seg_r', # 保存项目目录 + project='runs/train/60seg', # 保存项目目录 name='exp', # 实验名称 exist_ok=False, # 不覆盖已有实验 optimizer='AdamW', # 可选优化器 - lr0=0.0005, # 初始学习率 + lr0=0.0003, # 初始学习率 patience=0, # 早停轮数 ) \ No newline at end of file diff --git a/ultralytics_yolo11-main/train_seg_main.py b/ultralytics_yolo11-main/train_seg_r_main_61.py similarity index 68% rename from ultralytics_yolo11-main/train_seg_main.py rename to ultralytics_yolo11-main/train_seg_r_main_61.py index 61d6d48..fa0ee41 100644 --- a/ultralytics_yolo11-main/train_seg_main.py +++ b/ultralytics_yolo11-main/train_seg_r_main_61.py @@ -2,20 +2,20 @@ from ultralytics import YOLO if __name__ == '__main__': # ✅ 推荐:使用官方预训练分割模型 - #model = YOLO(r'/home/hx/yolo/ultralytics_yolo11-main/runs/train/seg_j/exp2/weights/best.pt') + #model = YOLO(r'/home/hx/yolo/ultralytics_yolo11-main/runs/train/61seg/exp2/weights/best.pt') model = YOLO(r'/home/hx/yolo/ultralytics_yolo11-main/ultralytics/cfg/models/11/yolo11-seg.yaml') # 开始训练 results = model.train( - data='data.yaml', # 数据配置文件 - epochs=200, # 训练轮数 + data='data_seg61.yaml', # 数据配置文件 + epochs=500, # 训练轮数 imgsz=1280, batch=4, # 每批图像数量 workers=10, # 数据加载线程数 device='0', # 使用 GPU 0 - project='runs/train/seg_02', # 保存项目目录 + project='runs/train/61seg', # 保存项目目录 name='exp', # 实验名称 exist_ok=False, # 不覆盖已有实验 optimizer='AdamW', # 可选优化器 - lr0=0.0005, # 初始学习率 - patience=20, # 早停轮数 + lr0=0.0005, # 初始学习率 + patience=0, # 早停轮数 ) \ No newline at end of file diff --git a/ultralytics_yolo11-main/ultralytics/cfg/models/11/yolo11-2.yaml b/ultralytics_yolo11-main/ultralytics/cfg/models/11/yolo11-2.yaml new file mode 100644 index 0000000..80e5677 --- /dev/null +++ b/ultralytics_yolo11-main/ultralytics/cfg/models/11/yolo11-2.yaml @@ -0,0 +1,47 @@ +# Ultralytics YOLO 🚀, AGPL-3.0 license +# YOLO11 object detection model with P3-P5 outputs. For Usage examples see https://docs.ultralytics.com/tasks/detect + +# Parameters +nc: 2 # number of classes +scales: # model compound scaling constants, i.e. 'model=yolo11n.yaml' will call yolo11.yaml with scale 'n' + # [depth, width, max_channels] + n: [0.50, 0.25, 1024] # summary: 319 layers, 2624080 parameters, 2624064 gradients, 6.6 GFLOPs + s: [0.50, 0.50, 1024] # summary: 319 layers, 9458752 parameters, 9458736 gradients, 21.7 GFLOPs + m: [0.50, 1.00, 512] # summary: 409 layers, 20114688 parameters, 20114672 gradients, 68.5 GFLOPs + l: [1.00, 1.00, 512] # summary: 631 layers, 25372160 parameters, 25372144 gradients, 87.6 GFLOPs + x: [1.00, 1.50, 512] # summary: 631 layers, 56966176 parameters, 56966160 gradients, 196.0 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, Detect, [nc]] # Detect(P3, P4, P5) diff --git a/ultralytics_yolo11-main/ultralytics/cfg/models/11/yolo11-2cls.yaml b/ultralytics_yolo11-main/ultralytics/cfg/models/11/yolo11-2cls.yaml new file mode 100644 index 0000000..5d56f49 --- /dev/null +++ b/ultralytics_yolo11-main/ultralytics/cfg/models/11/yolo11-2cls.yaml @@ -0,0 +1,30 @@ +# Ultralytics YOLO 🚀, AGPL-3.0 license +# YOLO11-cls image classification model. For Usage examples see https://docs.ultralytics.com/tasks/classify + +# Parameters +nc: 5 # number of classes +scales: # model compound scaling constants, i.e. 'model=yolo11n-cls.yaml' will call yolo11-cls.yaml with scale 'n' + # [depth, width, max_channels] + n: [0.50, 0.25, 1024] # summary: 151 layers, 1633584 parameters, 1633584 gradients, 3.3 GFLOPs + s: [0.50, 0.50, 1024] # summary: 151 layers, 5545488 parameters, 5545488 gradients, 12.2 GFLOPs + m: [0.50, 1.00, 512] # summary: 187 layers, 10455696 parameters, 10455696 gradients, 39.7 GFLOPs + l: [1.00, 1.00, 512] # summary: 309 layers, 12937104 parameters, 12937104 gradients, 49.9 GFLOPs + x: [1.00, 1.50, 512] # summary: 309 layers, 28458544 parameters, 28458544 gradients, 111.1 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, 2, C2PSA, [1024]] # 9 + +# YOLO11n head +head: + - [-1, 1, Classify, [nc]] # Classify diff --git a/ultralytics_yolo11-main/ultralytics/cfg/models/11/yolo11.yaml b/ultralytics_yolo11-main/ultralytics/cfg/models/11/yolo11.yaml index 80e5677..67dd779 100644 --- a/ultralytics_yolo11-main/ultralytics/cfg/models/11/yolo11.yaml +++ b/ultralytics_yolo11-main/ultralytics/cfg/models/11/yolo11.yaml @@ -2,7 +2,7 @@ # YOLO11 object detection model with P3-P5 outputs. For Usage examples see https://docs.ultralytics.com/tasks/detect # Parameters -nc: 2 # number of classes +nc: 1 # number of classes scales: # model compound scaling constants, i.e. 'model=yolo11n.yaml' will call yolo11.yaml with scale 'n' # [depth, width, max_channels] n: [0.50, 0.25, 1024] # summary: 319 layers, 2624080 parameters, 2624064 gradients, 6.6 GFLOPs diff --git a/yemian/lianghua_txt.py b/yemian/lianghua_txt.py index 3e084f4..210814f 100644 --- a/yemian/lianghua_txt.py +++ b/yemian/lianghua_txt.py @@ -2,10 +2,10 @@ import os # ================== 配置参数 ================== # 图片所在的文件夹路径 -image_folder = '/home/hx/yolo/yemian/resize_p' # 修改为你的图片文件夹路径 +image_folder = '/home/hx/yolo/yemian/61_lianghua' # 修改为你的图片文件夹路径 # 输出的txt文件路径 -output_txt = '/home/hx/yolo/yemian/image_list.txt' # 修改为你想保存的路径 +output_txt = '/home/hx/yolo/yemian/61_lianghua/image_list.txt' # 修改为你想保存的路径 # 支持的图片格式 image_extensions = {'.jpg', '.jpeg', '.png', '.bmp', '.webp', '.tiff', '.gif'} diff --git a/yemian/new.txt b/yemian/new.txt new file mode 100644 index 0000000..923a10b --- /dev/null +++ b/yemian/new.txt @@ -0,0 +1 @@ +670,623,465,178 \ No newline at end of file diff --git a/yemian/test_image/1.png b/yemian/test_image/1.png deleted file mode 100644 index 2a4c35a..0000000 Binary files a/yemian/test_image/1.png and /dev/null differ diff --git a/yemian/yemian_line/best.pt b/yemian/yemian_line/best.pt deleted file mode 100644 index f4b58f7..0000000 Binary files a/yemian/yemian_line/best.pt and /dev/null differ diff --git a/yemian/yemian_line/danmu_d.py b/yemian/yemian_line/danmu_d.py index 8c98a08..9dc1e86 100644 --- a/yemian/yemian_line/danmu_d.py +++ b/yemian/yemian_line/danmu_d.py @@ -1,170 +1,188 @@ +import os import cv2 import numpy as np from pathlib import Path from ultralytics import YOLO # -------------------- -# 配置参数 +# 配置 # -------------------- -IMAGE_PATH = "/home/hx/yolo/yemian/test_image/1.png" -MODEL_PATH = "best.pt" -OUTPUT_PATH = "./output/single_result.jpg" - TARGET_SIZE = 640 - -# 新增:用于计算像素到实际尺寸换算比例的函数 -def calculate_pixel_to_real_ratio(real_length_mm, pixel_length): - """ - 计算像素到实际尺寸的换算比例。 - 参数: - - real_length_mm: 实际物理长度(例如:毫米) - - pixel_length: 对应的实际物理长度在图像中的像素数 - 返回: - - PIXEL_TO_REAL_RATIO: 每个像素代表的实际物理长度(单位:mm/像素) - """ - if pixel_length == 0: - raise ValueError("像素长度不能为0") - return real_length_mm / pixel_length - - -# 在主函数infer_single_image之前设置一个默认值 -PIXEL_TO_REAL_RATIO = 1.0 # 默认值,之后会被真实计算的比例替换 - -# 假设我们知道某物体的真实长度是real_length_mm毫米,在图像中占pixel_length像素 -real_length_mm = 100 # 物体的实际长度(单位:毫米) -pixel_length = 200 # 物体在图像中的像素长度 - -try: - PIXEL_TO_REAL_RATIO = calculate_pixel_to_real_ratio(real_length_mm, pixel_length) - print(f"换算比例已设定: {PIXEL_TO_REAL_RATIO:.4f} mm/像素") -except ValueError as e: - print(e) - -# 全局 ROI 定义:(x, y, w, h) +IMAGE_DIR = "/media/hx/04e879fa-d697-4b02-ac7e-a4148876ebb0/dataset/1/612/train/class0" +MODEL_PATH = "/home/hx/yolo/ultralytics_yolo11-main/runs/train/61seg/exp2/weights/best.pt" +OUTPUT_DIR = "/media/hx/04e879fa-d697-4b02-ac7e-a4148876ebb0/dataset/1/612/train/class2" +''' ROIS = [ - (859, 810, 696, 328), + (445, 540, 931, 319), +] + +''' +ROIS = [ + (0, 0, 640, 640), ] # -------------------- -# 辅助函数(保持不变) +# 从 mask 中提取左右边界点 # -------------------- -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) - left_thresh = int(w * left_ratio) - right_thresh = w - int(w * right_ratio) +def extract_left_right_edge_points(mask_bin): + h, w = mask_bin.shape + left_pts = [] + right_pts = [] - 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 - 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=10): - 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 - if mask.sum() < 2: - return (m, b), pts - m, b = np.polyfit(y[mask], x[mask], 1) - inliers = np.stack([x[mask], y[mask]], axis=1) - return (m, b), inliers + for y in range(h): + xs = np.where(mask_bin[y] > 0)[0] + if len(xs) >= 2: + left_pts.append([xs.min(), y]) + right_pts.append([xs.max(), y]) + return np.array(left_pts), np.array(right_pts) # -------------------- -# 单图推理主函数 +# 按 seg 的 y 百分比筛选 # -------------------- -def infer_single_image(image_path, model_path, output_path): - orig_img = cv2.imread(str(image_path)) - if orig_img is None: - print(f"❌ 无法读取图像: {image_path}") +def filter_by_seg_y_ratio(pts, y_start=0.35, y_end=0.85): + if len(pts) < 2: + return pts + + y_min = pts[:, 1].min() + y_max = pts[:, 1].max() + h = y_max - y_min + if h < 10: + return pts + + y0 = y_min + int(h * y_start) + y1 = y_min + int(h * y_end) + + return pts[(pts[:, 1] >= y0) & (pts[:, 1] <= y1)] + +# -------------------- +# 拟合直线 +# -------------------- +def fit_line(pts): + if len(pts) < 2: return None + x = pts[:, 0] + y = pts[:, 1] + m, b = np.polyfit(y, x, 1) + return m, b - overlay_img = orig_img.copy() - x_diff_pixel = None # 像素单位的差值 +# -------------------- +# y 参考值(seg 底部) +# -------------------- +def get_y_ref(mask_bin): + h, w = mask_bin.shape + ys = [] + for x in range(int(w * 0.2), int(w * 0.8)): + y = np.where(mask_bin[:, x] > 0)[0] + if len(y): + ys.append(y.max()) + return int(np.mean(ys)) if ys else h // 2 - model = YOLO(model_path) - Path(output_path).parent.mkdir(parents=True, exist_ok=True) +# -------------------- +# 单图处理 +# -------------------- +def process_one(img_path, model): + img = cv2.imread(str(img_path)) + vis = 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)) + result_data = None # (XL, Y, XR, Y, diff) - results = model(source=resized_img, imgsz=TARGET_SIZE, verbose=False) - result = results[0] + for rx, ry, rw, rh in ROIS: + roi = img[ry:ry+rh, rx:rx+rw] + resized = cv2.resize(roi, (TARGET_SIZE, TARGET_SIZE)) - if result.masks is None or len(result.masks.data) == 0: - print("❌ 未检测到 mask") + result = model(resized, imgsz=TARGET_SIZE, verbose=False)[0] + if result.masks is None: continue 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_bin = cv2.resize(mask_bin, (rw, rh), cv2.INTER_NEAREST) - 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) + # overlay mask + green = np.zeros_like(roi) + green[mask_bin == 1] = (0, 255, 0) + vis[ry:ry+rh, rx:rx+rw] = cv2.addWeighted(roi, 0.7, green, 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 = extract_left_right_edge_points(mask_bin) + left_pts = filter_by_seg_y_ratio(left_pts) + right_pts = filter_by_seg_y_ratio(right_pts) - left_pts, right_pts = select_edge_corners(corners, w) - left_line, _ = fit_line_with_outlier_removal(left_pts) - right_line, _ = fit_line_with_outlier_removal(right_pts) + left_line = fit_line(left_pts) + right_line = fit_line(right_pts) + if left_line is None or right_line is None: + continue - if left_line and right_line: - y_ref = h * 0.6 - m1, b1 = left_line - m2, b2 = right_line - x1 = m1 * y_ref + b1 - x2 = m2 * y_ref + b2 - x_diff_pixel = abs(x2 - x1) + m1, b1 = left_line + m2, b2 = right_line - # 绘制参考线和文字(仍用像素值显示) - cv2.line(overlay_img[y:y+h, x:x+w], (0, int(y_ref)), (w, int(y_ref)), (0, 255, 255), 2) - cv2.putText(overlay_img[y:y+h, x:x+w], - f"x_diff={x_diff_pixel:.1f}px", - (10, 30), cv2.FONT_HERSHEY_SIMPLEX, - 1, (0, 255, 255), 2) + y_ref = get_y_ref(mask_bin) - # 绘制左右拟合线 - for (m, b), color in [(left_line, (0, 0, 255)), (right_line, (255, 0, 0))]: - y1, y2 = 0, h - x1_line, x2_line = int(m * y1 + b), int(m * y2 + b) - cv2.line(overlay_img[y:y+h, x:x+w], (x1_line, y1), (x2_line, y2), color, 3) + # ROI 坐标 + x_left = int(m1 * y_ref + b1) + x_right = int(m2 * y_ref + b2) - cv2.imwrite(output_path, overlay_img) - print(f"✅ 结果已保存至: {output_path}") + # 🔴 全局坐标 + X_L = rx + x_left + X_R = rx + x_right + Y = ry + y_ref - if x_diff_pixel is not None: - x_diff_real = x_diff_pixel * PIXEL_TO_REAL_RATIO - print(f"📊 x差值(像素) = {x_diff_pixel:.2f} px") - print(f"📏 x差值(实际) = {x_diff_real:.2f} mm") # 可改为 cm 或其他单位 - else: - print("⚠️ 未能计算 x 差值") + diff = X_R - X_L - return x_diff_pixel + result_data = (X_L, Y, X_R, Y, diff) + # ---------- 可视化 ---------- + roi_vis = vis[ry:ry+rh, rx:rx+rw] -# ===================== -# 运行入口 -# ===================== + for (m, b), c in [((m1, b1), (0,0,255)), ((m2, b2), (255,0,0))]: + cv2.line( + roi_vis, + (int(m * 0 + b), 0), + (int(m * rh + b), rh), + c, 3 + ) + + cv2.line(roi_vis, (0, y_ref), (rw, y_ref), (0,255,255), 2) + cv2.circle(roi_vis, (x_left, y_ref), 6, (0,0,255), -1) + cv2.circle(roi_vis, (x_right, y_ref), 6, (255,0,0), -1) + + cv2.putText( + roi_vis, + f"diff={diff}px", + (10, 40), + cv2.FONT_HERSHEY_SIMPLEX, + 1, + (0,255,255), + 2 + ) + + return vis, result_data + +# -------------------- +# 批处理 +# -------------------- +def run(): + model = YOLO(MODEL_PATH) + Path(OUTPUT_DIR).mkdir(exist_ok=True) + + for img in sorted(os.listdir(IMAGE_DIR)): + if not img.lower().endswith((".jpg", ".png", ".jpeg")): + continue + + vis, data = process_one(Path(IMAGE_DIR) / img, model) + out = Path(OUTPUT_DIR) / f"vis_{img}" + cv2.imwrite(str(out), vis) + + if data: + XL, YL, XR, YR, diff = data + print(f"[{img}]") + print(f" 左交点: ({XL}, {YL})") + print(f" 右交点: ({XR}, {YR})") + print(f" diff : {diff} px") + else: + print(f"[{img}] 无有效结果") + +# -------------------- if __name__ == "__main__": - infer_single_image(IMAGE_PATH, MODEL_PATH, OUTPUT_PATH) \ No newline at end of file + run() diff --git a/yemian/yemian_line/seg_bushu/11.jpg b/yemian/yemian_line/seg_bushu/11.jpg deleted file mode 100755 index 9455647..0000000 Binary files a/yemian/yemian_line/seg_bushu/11.jpg and /dev/null differ diff --git a/yemian/yemian_line/seg_bushu/danmu_bushu.py b/yemian/yemian_line/seg_bushu/danmu_bushu.py index 5b81c5e..86a5b14 100644 --- a/yemian/yemian_line/seg_bushu/danmu_bushu.py +++ b/yemian/yemian_line/seg_bushu/danmu_bushu.py @@ -1,243 +1,143 @@ import cv2 import numpy as np -from pathlib import Path from rknnlite.api import RKNNLite -# -------------------- -# 配置参数 -# -------------------- -IMAGE_PATH = "./11.jpg" +# ====================== +# 配置 +# ====================== +IMAGE_PATH = "3.png" MODEL_PATH = "segr.rknn" -OUTPUT_PATH = "./single_result.jpg" -TARGET_SIZE = 640 +OUT_OVERLAY = "result_overlay.jpg" +DEBUG_INPUT = "debug_input_roi.png" +DEBUG_PROTO = "debug_proto_mask.png" +DEBUG_INST_PROTO = "debug_inst_proto.png" -# 像素到实际尺寸换算比例 -def calculate_pixel_to_real_ratio(real_length_mm, pixel_length): - if pixel_length == 0: - raise ValueError("像素长度不能为0") - return real_length_mm / pixel_length +IMG_SIZE = 640 +OBJ_THRESH = 0.25 +MASK_THRESH = 0.5 +STRIDES = [8, 16, 32] -PIXEL_TO_REAL_RATIO = 1.0 -real_length_mm = 100 # 实际长度(毫米) -pixel_length = 200 # 对应像素长度 -try: - PIXEL_TO_REAL_RATIO = calculate_pixel_to_real_ratio(real_length_mm, pixel_length) - print(f"换算比例已设定: {PIXEL_TO_REAL_RATIO:.4f} mm/像素") -except ValueError as e: - print(e) - -# 全局 ROI 定义:(x, y, w, h) ROIS = [ - (859, 810, 696, 328), + (670, 623, 465, 178), ] -# -------------------- -# RKNN 单例管理 -# -------------------- -_rknn_instance = None +# ====================== +# 工具函数 +# ====================== +def sigmoid(x): + return 1 / (1 + np.exp(-x)) -def init_rknn(model_path): - global _rknn_instance - if _rknn_instance is not None: - return _rknn_instance - _rknn_instance = RKNNLite(verbose=False) - ret = _rknn_instance.load_rknn(model_path) - if ret != 0: - print(f"[ERROR] 加载 RKNN 模型失败: {ret}") - _rknn_instance = None - return None +def resize_to_640(img): + """seg 专用:禁止 letterbox,直接 resize""" + return cv2.resize(img, (IMG_SIZE, IMG_SIZE), interpolation=cv2.INTER_LINEAR) - ret = _rknn_instance.init_runtime(core_mask=RKNNLite.NPU_CORE_1) - if ret != 0: - print(f"[ERROR] 初始化 NPU 失败: {ret}") - _rknn_instance.release() - _rknn_instance = None - return None - print("[✅] RKNN 分割模型加载成功") - return _rknn_instance +def dfl_decode(dfl): + bins = np.arange(16) + dfl = sigmoid(dfl) + dfl /= np.sum(dfl, axis=1, keepdims=True) + return np.sum(dfl * bins, axis=1) -def release_rknn(): - global _rknn_instance - if _rknn_instance: - _rknn_instance.release() - _rknn_instance = None - print("[INFO] RKNN 模型已释放") -# -------------------- -# 工具函数:letterbox resize -# -------------------- -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), interpolation=cv2.INTER_LINEAR) - canvas = np.full((target_h, target_w, 3), bg_color, dtype=np.uint8) - dx = (target_w - new_w) // 2 - dy = (target_h - new_h) // 2 - canvas[dy:dy+new_h, dx:dx+new_w] = resized - return canvas, scale, dx, dy +def largest_cc(mask): + num, labels = cv2.connectedComponents(mask.astype(np.uint8)) + if num <= 1: + return mask + areas = [(labels == i).sum() for i in range(1, num)] + return (labels == (np.argmax(areas) + 1)).astype(np.uint8) -# -------------------- -# 辅助函数 -# -------------------- -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) - 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] +# ====================== +# 单 ROI 推理(完整语义 mask) +# ====================== +def infer_single_roi(rknn, roi): + h0, w0 = roi.shape[:2] - 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 + # ---------- 1️⃣ 正确的 seg 输入 ---------- + inp_img = resize_to_640(roi) + cv2.imwrite(DEBUG_INPUT, inp_img) - left_final = filter_by_y_variation(left_candidates) - right_final = filter_by_y_variation(right_candidates) - return left_final, right_final + inp = inp_img[..., ::-1][None, ...] -def fit_line_with_outlier_removal(pts, dist_thresh=10): - 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 - if mask.sum() < 2: - return (m, b), pts - m, b = np.polyfit(y[mask], x[mask], 1) - inliers = np.stack([x[mask], y[mask]], axis=1) - return (m, b), inliers + outputs = rknn.inference([inp]) -# -------------------- -# RKNN 推理生成 mask(增加去除小区域) -# -------------------- -def get_mask_from_rknn(rknn_model, roi_img, target_size=640, min_area=100): - h_orig, w_orig = roi_img.shape[:2] - preprocessed, scale, dx, dy = letterbox_resize(roi_img, (target_size, target_size)) - infer_input = preprocessed[np.newaxis, :, :, ::-1].astype(np.float32) + # ---------- 2️⃣ proto ---------- + proto = outputs[12][0] # (32,160,160) - try: - outputs = rknn_model.inference(inputs=[infer_input]) - except Exception as e: - print(f"[ERROR] RKNN 推理异常: {e}") - return None + best_score = -1 + best_coef = None - try: - proto = outputs[12][0] # (32,160,160) - mask_proto = np.mean(proto, axis=0) - mask_proto = 1 / (1 + np.exp(-mask_proto)) + out_i = 0 + for stride in STRIDES: + reg = outputs[out_i][0] + cls = outputs[out_i + 1][0, 0] + obj = outputs[out_i + 2][0, 0] + coef = outputs[out_i + 3][0] + out_i += 4 - mask_lb = cv2.resize(mask_proto, (target_size, target_size), interpolation=cv2.INTER_LINEAR) + score_map = sigmoid(cls) * sigmoid(obj) + y, x = np.unravel_index(np.argmax(score_map), score_map.shape) + score = score_map[y, x] - scale = min(target_size / w_orig, target_size / h_orig) - new_w, new_h = int(w_orig*scale), int(h_orig*scale) - pad_x = (target_size - new_w)//2 - pad_y = (target_size - new_h)//2 - mask_cropped = mask_lb[pad_y:pad_y+new_h, pad_x:pad_x+new_w] - - mask_resized = cv2.resize(mask_cropped, (w_orig, h_orig), interpolation=cv2.INTER_LINEAR) - mask_bin = (mask_resized > 0.5).astype(np.uint8) - - # 去除小区域 - num_labels, labels, stats, _ = cv2.connectedComponentsWithStats(mask_bin, connectivity=8) - mask_clean = np.zeros_like(mask_bin) - for i in range(1, num_labels): - area = stats[i, cv2.CC_STAT_AREA] - if area >= min_area: - mask_clean[labels == i] = 1 - - return mask_clean - except Exception as e: - print(f"[ERROR] 生成 mask 失败: {e}") - return None - -# -------------------- -# 主函数 -# -------------------- -def infer_single_image(image_path, model_path, output_path): - orig_img = cv2.imread(str(image_path)) - if orig_img is None: - print(f"无法读取图像: {image_path}") - return None - - overlay_img = orig_img.copy() - x_diff_pixel = None - - rknn = init_rknn(model_path) - if rknn is None: - print("RKNN 初始化失败") - return None - - Path(output_path).parent.mkdir(parents=True, exist_ok=True) - - for idx, (x, y, w, h) in enumerate(ROIS): - roi_img = orig_img[y:y+h, x:x+w] - - mask_bin = get_mask_from_rknn(rknn, roi_img, target_size=TARGET_SIZE, min_area=30000) - if mask_bin is None or mask_bin.sum() == 0: - print("未检测到有效 mask") + if score < OBJ_THRESH or score <= best_score: continue - 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) + best_score = score + best_coef = coef[:, y, x] - mask_gray = (mask_bin * 255).astype(np.uint8) - corners = cv2.goodFeaturesToTrack(mask_gray, maxCorners=200, qualityLevel=0.01, minDistance=5) + if best_coef is None: + return None - left_pts, right_pts = select_edge_corners(corners, w) - left_line, _ = fit_line_with_outlier_removal(left_pts) - right_line, _ = fit_line_with_outlier_removal(right_pts) + # ---------- 3️⃣ proto_mask(完整) ---------- + proto_mask = sigmoid(np.tensordot(best_coef, proto, axes=1)) # (160,160) - if left_line and right_line: - y_ref = h * 0.6 - m1, b1 = left_line - m2, b2 = right_line - x1 = m1 * y_ref + b1 - x2 = m2 * y_ref + b2 - x_diff_pixel = abs(x2 - x1) + pm = (proto_mask - proto_mask.min()) / (proto_mask.max() - proto_mask.min() + 1e-6) + cv2.imwrite(DEBUG_PROTO, (pm * 255).astype(np.uint8)) - cv2.line(overlay_img[y:y+h, x:x+w], (0, int(y_ref)), (w, int(y_ref)), (0, 255, 255), 2) - cv2.putText(overlay_img[y:y+h, x:x+w], - f"x_diff={x_diff_pixel:.1f}px", - (10, 30), cv2.FONT_HERSHEY_SIMPLEX, - 1, (0, 255, 255), 2) + # ---------- 4️⃣ 二值化 + 最大连通域(不裁!) ---------- + inst_proto = (proto_mask > MASK_THRESH).astype(np.uint8) + inst_proto = largest_cc(inst_proto) - for (m, b), color in [(left_line, (0, 0, 255)), (right_line, (255, 0, 0))]: - y1, y2 = 0, h - x1_line, x2_line = int(m * y1 + b), int(m * y2 + b) - cv2.line(overlay_img[y:y+h, x:x+w], (x1_line, y1), (x2_line, y2), color, 3) + cv2.imwrite(DEBUG_INST_PROTO, inst_proto * 255) - cv2.imwrite(output_path, overlay_img) - print(f"结果已保存至: {output_path}") + # ---------- 5️⃣ proto → ROI ---------- + inst_roi = cv2.resize( + inst_proto, (w0, h0), interpolation=cv2.INTER_NEAREST + ) - if x_diff_pixel is not None: - x_diff_real = x_diff_pixel * PIXEL_TO_REAL_RATIO - print(f"x差值(像素) = {x_diff_pixel:.2f} px") - print(f"x差值(实际) = {x_diff_real:.2f} mm") - else: - print("未能计算 x 差值") + return inst_roi * 255 + + +# ====================== +# 主程序 +# ====================== +def main(): + img = cv2.imread(IMAGE_PATH) + overlay = img.copy() + + rknn = RKNNLite() + rknn.load_rknn(MODEL_PATH) + rknn.init_runtime() + + for (x, y, w, h) in ROIS: + roi = img[y:y + h, x:x + w] + mask = infer_single_roi(rknn, roi) + if mask is None: + continue + + color = np.zeros_like(roi) + color[mask == 255] = (0, 255, 0) + + overlay[y:y + h, x:x + w] = cv2.addWeighted( + roi, 0.7, color, 0.3, 0 + ) + + rknn.release() + cv2.imwrite(OUT_OVERLAY, overlay) + print("✅ 完成:", OUT_OVERLAY) - release_rknn() - return x_diff_pixel -# ===================== -# 运行入口 -# ===================== if __name__ == "__main__": - infer_single_image(IMAGE_PATH, MODEL_PATH, OUTPUT_PATH) - + main() \ No newline at end of file diff --git a/yolo11_detect/annotations.xml b/yolo11_detect/annotations.xml new file mode 100644 index 0000000..7cdc7d8 --- /dev/null +++ b/yolo11_detect/annotations.xml @@ -0,0 +1,292 @@ + + + 1.1 + + + 282 + detect12.31 + 64 + annotation + 0 + + 2025-12-31 01:23:25.627596+00:00 + 2025-12-31 01:50:37.361043+00:00 + default + 0 + 63 + + + + 195 + 0 + 63 + http://www.xj-robot.com:9000/api/jobs/195 + + + + huangxin + 2193534909@qq.com + + + + + + + + + + 2025-12-31 01:50:58.885062+00:00 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/YOLO—detect/cvattodetect.py b/yolo11_detect/cvattodetect.py similarity index 97% rename from YOLO—detect/cvattodetect.py rename to yolo11_detect/cvattodetect.py index c0b3cf7..f5d5add 100644 --- a/YOLO—detect/cvattodetect.py +++ b/yolo11_detect/cvattodetect.py @@ -84,13 +84,13 @@ if __name__ == "__main__": OUTPUT_LABELS_DIR = "labels" # 输出的 YOLO .txt 目录 # 方式1:自动从 XML 提取类别(推荐) - CLASS_MAP = None + #CLASS_MAP = None # 方式2:手动指定(确保与训练时一致) - # CLASS_MAP = { - # "hole": 0, - # "crack": 1 - # } + CLASS_MAP = { + "bag": 0, + "bag35": 1, + } # ====== 执行转换 ====== cvat_to_yolo_detect( diff --git a/yolo11_detect/trans_detecttocvat.py b/yolo11_detect/trans_detecttocvat.py new file mode 100644 index 0000000..9f57c78 --- /dev/null +++ b/yolo11_detect/trans_detecttocvat.py @@ -0,0 +1,141 @@ +import os +import xml.etree.ElementTree as ET +from pathlib import Path + + +def yolo_detect_to_cvat( + images_dir, + labels_dir, + output_xml, + class_id_to_name, +): + """ + 将 YOLO Detect 格式转换为 CVAT XML(目标检测 box) + + Args: + images_dir (str): 图片目录 + labels_dir (str): YOLO txt 标注目录 + output_xml (str): 输出 CVAT XML 路径 + class_id_to_name (dict): {0: "xxx", 1: "yyy"} + """ + + images_dir = Path(images_dir) + labels_dir = Path(labels_dir) + + # ---------------------------- + # 创建 XML 结构 + # ---------------------------- + annotations = ET.Element("annotations") + + version = ET.SubElement(annotations, "version") + version.text = "1.1" + + # labels + meta = ET.SubElement(annotations, "meta") + task = ET.SubElement(meta, "task") + labels_elem = ET.SubElement(task, "labels") + + for class_id, name in class_id_to_name.items(): + label = ET.SubElement(labels_elem, "label") + name_elem = ET.SubElement(label, "name") + name_elem.text = name + + image_id = 0 + + # ---------------------------- + # 遍历图片 + # ---------------------------- + for img_path in sorted(images_dir.iterdir()): + if img_path.suffix.lower() not in [".jpg", ".png", ".jpeg"]: + continue + + import cv2 + img = cv2.imread(str(img_path)) + if img is None: + print(f"⚠️ 无法读取图片: {img_path}") + continue + + height, width = img.shape[:2] + + image_elem = ET.SubElement( + annotations, + "image", + { + "id": str(image_id), + "name": img_path.name, + "width": str(width), + "height": str(height), + } + ) + + label_txt = labels_dir / f"{img_path.stem}.txt" + + # ---------------------------- + # 有 / 无标签都要建 image + # ---------------------------- + if label_txt.exists(): + with open(label_txt, "r") as f: + for line in f: + line = line.strip() + if not line: + continue + + class_id, xc, yc, w, h = map(float, line.split()) + class_id = int(class_id) + + label_name = class_id_to_name.get(class_id) + if label_name is None: + print(f"⚠️ 未知 class_id: {class_id}") + continue + + # YOLO → CVAT 坐标 + box_w = w * width + box_h = h * height + xtl = xc * width - box_w / 2 + ytl = yc * height - box_h / 2 + xbr = xtl + box_w + ybr = ytl + box_h + + ET.SubElement( + image_elem, + "box", + { + "label": label_name, + "xtl": f"{xtl:.2f}", + "ytl": f"{ytl:.2f}", + "xbr": f"{xbr:.2f}", + "ybr": f"{ybr:.2f}", + "occluded": "0", + "source": "manual", + } + ) + + image_id += 1 + print(f"✅ {img_path.name}") + + # ---------------------------- + # 写 XML + # ---------------------------- + tree = ET.ElementTree(annotations) + tree.write(output_xml, encoding="utf-8", xml_declaration=True) + + print(f"\n🎉 转换完成,CVAT XML 已生成:{output_xml}") + +if __name__ == "__main__": + IMAGES_DIR = "/media/hx/04e879fa-d697-4b02-ac7e-a4148876ebb0/dataset/detect/1" + LABELS_DIR = "/media/hx/04e879fa-d697-4b02-ac7e-a4148876ebb0/dataset/detect/1" + OUTPUT_XML = "annotations.xml" + + # ⚠️ 一定要和 YOLO 训练时一致 + CLASS_ID_TO_NAME = { + #0: "hole", + #1: "crack" + 0: "bag" + } + + yolo_detect_to_cvat( + images_dir=IMAGES_DIR, + labels_dir=LABELS_DIR, + output_xml=OUTPUT_XML, + class_id_to_name=CLASS_ID_TO_NAME + ) diff --git a/yolo11_obb/txt_1.py b/yolo11_obb/txt_1.py index 4562401..9083573 100644 --- a/yolo11_obb/txt_1.py +++ b/yolo11_obb/txt_1.py @@ -2,10 +2,10 @@ import os # ================== 配置参数 ================== # 图片所在的文件夹路径 -image_folder = '/home/hx/开发/ML_xiantiao/image/lianghua' # 修改为你的图片文件夹路径 +image_folder = '/media/hx/04e879fa-d697-4b02-ac7e-a4148876ebb0/dataset/ailaidete/bag' # 修改为你的图片文件夹路径 # 输出的txt文件路径 -output_txt = '/home/hx/开发/ML_xiantiao/image/lianghua/image_list.txt' # 修改为你想保存的路径 +output_txt = '/media/hx/04e879fa-d697-4b02-ac7e-a4148876ebb0/dataset/ailaidete/bag/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..4938210 --- /dev/null +++ b/yolo11_point/annotations.xml @@ -0,0 +1,1642 @@ + + + 1.1 + + + 278 + 12.26 + 267 + annotation + 0 + + 2025-12-30 01:56:27.692331+00:00 + 2025-12-30 03:25:30.358671+00:00 + default + 0 + 266 + + + + 191 + 0 + 266 + http://www.xj-robot.com:9000/api/jobs/191 + + + + huangxin + 2193534909@qq.com + + + + + + + + + + 2026-01-06 01:36:04.684356+00:00 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/yolo11_point/trans_cvattopoint.py b/yolo11_point/trans_cvattopoint.py index fe49750..40c72e1 100644 --- a/yolo11_point/trans_cvattopoint.py +++ b/yolo11_point/trans_cvattopoint.py @@ -3,7 +3,7 @@ import os # =================== 配置 =================== xml_file = 'annotations.xml' # 你的 CVAT XML 文件路径 -images_dir = '/media/hx/04e879fa-d697-4b02-ac7e-a4148876ebb0/dataset/1/20251209' # 图像文件夹(用于读取宽高) +images_dir = '/media/hx/04e879fa-d697-4b02-ac7e-a4148876ebb0/dataset/1/20251226' # 图像文件夹(用于读取宽高) output_dir = 'labels_keypoints' # 输出 YOLO 标签目录 os.makedirs(output_dir, exist_ok=True) diff --git a/yolo11_seg/2.png b/yolo11_seg/2.png new file mode 100644 index 0000000..90c8dd3 Binary files /dev/null and b/yolo11_seg/2.png differ diff --git a/yolo11_seg/3.png b/yolo11_seg/3.png new file mode 100644 index 0000000..cf854a2 Binary files /dev/null and b/yolo11_seg/3.png differ diff --git a/yolo11_seg/60seg.pt b/yolo11_seg/60seg.pt new file mode 100644 index 0000000..2a2563a Binary files /dev/null and b/yolo11_seg/60seg.pt differ diff --git a/yolo11_seg/annotations.xml b/yolo11_seg/annotations.xml new file mode 100644 index 0000000..980fbf2 --- /dev/null +++ b/yolo11_seg/annotations.xml @@ -0,0 +1,5781 @@ + + + 1.1 + + + 296 + zj_seg_61_0113 + 1431 + annotation + 0 + + 2026-01-14 01:53:43.655582+00:00 + 2026-01-14 07:18:37.516062+00:00 + default + 0 + 1430 + + + + 207 + 0 + 1430 + http://www.xj-robot.com:9000/api/jobs/207 + + + + huangxin + 2193534909@qq.com + + + + + + + + + 2026-01-14 07:42:43.524783+00:00 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/yolo11_seg/cvattoseg.py b/yolo11_seg/cvattoseg.py new file mode 100644 index 0000000..96dcb1c --- /dev/null +++ b/yolo11_seg/cvattoseg.py @@ -0,0 +1,106 @@ + +import os +import xml.etree.ElementTree as ET +from pathlib import Path + + +def cvat_to_yolo_seg( + xml_path, + output_dir, + class_name_to_id=None, + force_class_id=None +): + """ + 将 CVAT 导出的 XML(polygon / segmentation)转换为 YOLO Segmentation 格式 + + Args: + xml_path (str): CVAT 导出的 XML 文件路径 + output_dir (str): 输出 .txt 标注文件的目录 + class_name_to_id (dict, optional): + 类别名到 ID 的映射 + force_class_id (dict, optional): + 强制指定某些类别的 ID,例如 {"yemian": 0} + """ + + output_dir = Path(output_dir) + output_dir.mkdir(parents=True, exist_ok=True) + + tree = ET.parse(xml_path) + root = tree.getroot() + + # ---------------------------- + # 自动提取类别映射 + # ---------------------------- + if class_name_to_id is None: + class_name_to_id = {} + labels_elem = root.find(".//labels") + if labels_elem is not None: + for idx, label in enumerate(labels_elem.findall("label")): + name = label.find("name").text + class_name_to_id[name] = idx + else: + raise RuntimeError("❌ 未找到 ,请手动提供 class_name_to_id") + + print(f"原始类别映射: {class_name_to_id}") + + # ---------------------------- + # 强制修改类别 ID(新增功能) + # ---------------------------- + if force_class_id: + for name, new_id in force_class_id.items(): + if name in class_name_to_id: + old_id = class_name_to_id[name] + class_name_to_id[name] = new_id + print(f"强制修改类别映射: {name} {old_id} → {new_id}") + else: + print(f"类别 {name} 不存在,跳过") + + print(f"最终类别映射: {class_name_to_id}") + + # ---------------------------- + # 遍历每一张 image + # ---------------------------- + for image in root.findall("image"): + img_name = image.get("name") + width = int(image.get("width")) + height = int(image.get("height")) + + txt_path = output_dir / (Path(img_name).stem + ".txt") + lines = [] + + for polygon in image.findall("polygon"): + label = polygon.get("label") + if label not in class_name_to_id: + continue + + class_id = class_name_to_id[label] + points_str = polygon.get("points") + + points = [] + for p in points_str.strip().split(";"): + x, y = p.split(",") + x = float(x) / width + y = float(y) / height + points.append(f"{x:.6f}") + points.append(f"{y:.6f}") + + if len(points) < 6: + continue + + lines.append(f"{class_id} " + " ".join(points)) + + with open(txt_path, "w", encoding="utf-8") as f: + f.write("\n".join(lines)) + + print("✅ CVAT segmentation → YOLO seg 转换完成") + + +if __name__ == "__main__": + + cvat_to_yolo_seg( + xml_path="annotations.xml", + output_dir="labels_seg", + force_class_id={ + "yemian": 0 + } + ) diff --git a/yolo11_seg/test.png b/yolo11_seg/test.png new file mode 100644 index 0000000..e69de29 diff --git a/yolo11_seg/yolo_seg_infer_vis60.py b/yolo11_seg/yolo_seg_infer_vis60.py new file mode 100644 index 0000000..10b36c8 --- /dev/null +++ b/yolo11_seg/yolo_seg_infer_vis60.py @@ -0,0 +1,141 @@ +import cv2 +import numpy as np +import os +from ultralytics import YOLO + +# ---------------- 配置 ---------------- +MODEL_PATH = "60seg.pt" +IMAGE_PATH = "3.png" +OUT_DIR = "outputs" +IMG_SIZE = 640 +CONF_THRES = 0.25 +ALPHA = 0.5 +# ------------------------------------- + + +def get_color(idx): + np.random.seed(idx) + return tuple(int(x) for x in np.random.randint(0, 255, 3)) + + +def draw_segmentation(frame, result): + """ + 原样保留你的可视化逻辑 + """ + overlay = frame.copy() + + if result.masks is None: + return frame + + boxes = result.boxes + names = result.names + + for i, poly in enumerate(result.masks.xy): + cls_id = int(boxes.cls[i]) + conf = float(boxes.conf[i]) + + if conf < CONF_THRES: + continue + + color = get_color(cls_id) + poly = poly.astype(np.int32) + + # 填充 mask + cv2.fillPoly(overlay, [poly], color) + + # 轮廓 + cv2.polylines(overlay, [poly], True, color, 2) + + # 标签 + x, y = poly[0] + label = f"{names[cls_id]} {conf:.2f}" + cv2.putText( + overlay, + label, + (x, max(y - 5, 20)), + cv2.FONT_HERSHEY_SIMPLEX, + 0.6, + color, + 2 + ) + + return cv2.addWeighted(overlay, ALPHA, frame, 1 - ALPHA, 0) + + +def save_masks_as_yolo_seg(result, img_shape, save_path): + """ + 保存 YOLO segmentation 标注(txt) + 格式: + class_id x1 y1 x2 y2 ...(全部归一化) + """ + if result.masks is None: + return + + h, w = img_shape[:2] + boxes = result.boxes + + lines = [] + + for i, poly in enumerate(result.masks.xy): + cls_id = int(boxes.cls[i]) + conf = float(boxes.conf[i]) + + if conf < CONF_THRES: + continue + + poly_norm = [] + for x, y in poly: + poly_norm.append(f"{x / w:.6f}") + poly_norm.append(f"{y / h:.6f}") + + line = str(cls_id) + " " + " ".join(poly_norm) + lines.append(line) + + if lines: + with open(save_path, "w") as f: + f.write("\n".join(lines)) + + +def run_image_inference(): + os.makedirs(OUT_DIR, exist_ok=True) + + # 加载模型 + model = YOLO(MODEL_PATH) + + # 读取图片 + img = cv2.imread(IMAGE_PATH) + if img is None: + raise FileNotFoundError(f"❌ 无法读取图片: {IMAGE_PATH}") + + # 推理 + results = model( + img, + imgsz=IMG_SIZE, + conf=CONF_THRES, + verbose=False + ) + + result = results[0] + + # 1️⃣ 保存可视化结果 + vis = draw_segmentation(img, result) + out_img_path = os.path.join( + OUT_DIR, + os.path.splitext(os.path.basename(IMAGE_PATH))[0] + "_seg.png" + ) + cv2.imwrite(out_img_path, vis) + + # 2️⃣ 保存 YOLO segmentation 标注 + out_txt_path = os.path.join( + OUT_DIR, + os.path.splitext(os.path.basename(IMAGE_PATH))[0] + ".txt" + ) + save_masks_as_yolo_seg(result, img.shape, out_txt_path) + + print("✅ 推理完成") + print(f"🖼 可视化结果: {out_img_path}") + print(f"📄 YOLO Seg 标注: {out_txt_path}") + + +if __name__ == "__main__": + run_image_inference() diff --git a/yolo11_seg/yolo_seg_infer_vis61.py b/yolo11_seg/yolo_seg_infer_vis61.py new file mode 100644 index 0000000..f0e3495 --- /dev/null +++ b/yolo11_seg/yolo_seg_infer_vis61.py @@ -0,0 +1,141 @@ +import cv2 +import numpy as np +import os +from ultralytics import YOLO + +# ---------------- 配置 ---------------- +MODEL_PATH = "/home/hx/yolo/ultralytics_yolo11-main/runs/train/61seg/exp/weights/best.pt" +IMAGE_PATH = "2.png" +OUT_DIR = "outputs" +IMG_SIZE = 640 +CONF_THRES = 0.25 +ALPHA = 0.5 +# ------------------------------------- + + +def get_color(idx): + np.random.seed(idx) + return tuple(int(x) for x in np.random.randint(0, 255, 3)) + + +def draw_segmentation(frame, result): + """ + 原样保留你的可视化逻辑 + """ + overlay = frame.copy() + + if result.masks is None: + return frame + + boxes = result.boxes + names = result.names + + for i, poly in enumerate(result.masks.xy): + cls_id = int(boxes.cls[i]) + conf = float(boxes.conf[i]) + + if conf < CONF_THRES: + continue + + color = get_color(cls_id) + poly = poly.astype(np.int32) + + # 填充 mask + cv2.fillPoly(overlay, [poly], color) + + # 轮廓 + cv2.polylines(overlay, [poly], True, color, 2) + + # 标签 + x, y = poly[0] + label = f"{names[cls_id]} {conf:.2f}" + cv2.putText( + overlay, + label, + (x, max(y - 5, 20)), + cv2.FONT_HERSHEY_SIMPLEX, + 0.6, + color, + 2 + ) + + return cv2.addWeighted(overlay, ALPHA, frame, 1 - ALPHA, 0) + + +def save_masks_as_yolo_seg(result, img_shape, save_path): + """ + 保存 YOLO segmentation 标注(txt) + 格式: + class_id x1 y1 x2 y2 ...(全部归一化) + """ + if result.masks is None: + return + + h, w = img_shape[:2] + boxes = result.boxes + + lines = [] + + for i, poly in enumerate(result.masks.xy): + cls_id = int(boxes.cls[i]) + conf = float(boxes.conf[i]) + + if conf < CONF_THRES: + continue + + poly_norm = [] + for x, y in poly: + poly_norm.append(f"{x / w:.6f}") + poly_norm.append(f"{y / h:.6f}") + + line = str(cls_id) + " " + " ".join(poly_norm) + lines.append(line) + + if lines: + with open(save_path, "w") as f: + f.write("\n".join(lines)) + + +def run_image_inference(): + os.makedirs(OUT_DIR, exist_ok=True) + + # 加载模型 + model = YOLO(MODEL_PATH) + + # 读取图片 + img = cv2.imread(IMAGE_PATH) + if img is None: + raise FileNotFoundError(f"❌ 无法读取图片: {IMAGE_PATH}") + + # 推理 + results = model( + img, + imgsz=IMG_SIZE, + conf=CONF_THRES, + verbose=False + ) + + result = results[0] + + # 1️⃣ 保存可视化结果 + vis = draw_segmentation(img, result) + out_img_path = os.path.join( + OUT_DIR, + os.path.splitext(os.path.basename(IMAGE_PATH))[0] + "_seg.png" + ) + cv2.imwrite(out_img_path, vis) + + # 2️⃣ 保存 YOLO segmentation 标注 + out_txt_path = os.path.join( + OUT_DIR, + os.path.splitext(os.path.basename(IMAGE_PATH))[0] + ".txt" + ) + save_masks_as_yolo_seg(result, img.shape, out_txt_path) + + print("✅ 推理完成") + print(f"🖼 可视化结果: {out_img_path}") + print(f"📄 YOLO Seg 标注: {out_txt_path}") + + +if __name__ == "__main__": + run_image_inference() diff --git a/yolo11_seg/yolo_seg_infer_vis—60f.py b/yolo11_seg/yolo_seg_infer_vis—60f.py new file mode 100644 index 0000000..829f893 --- /dev/null +++ b/yolo11_seg/yolo_seg_infer_vis—60f.py @@ -0,0 +1,166 @@ +import cv2 +import numpy as np +import os +from ultralytics import YOLO + +# ================= 配置 ================= +MODEL_PATH = "/home/hx/yolo/ultralytics_yolo11-main/runs/train/60seg/exp3/weights/best.pt" +IMAGE_DIR = "/media/hx/04e879fa-d697-4b02-ac7e-a4148876ebb0/dataset/1/分割60/class4/1" +OUT_DIR = "./outputs" + +IMG_SIZE = 640 +CONF_THRES = 0.25 + +# 多边形简化比例(点数控制核心参数) +EPSILON_RATIO = 0.001 + +IMG_EXTS = (".jpg", ".jpeg", ".png", ".bmp") + +# -------- 保存开关 -------- +SAVE_LABELS = True # 是否保存 YOLO seg 标签 +SAVE_VIS = True # 是否保存可视化结果 +# ====================================== + + +def simplify_polygon(poly, epsilon_ratio): + """使用 approxPolyDP 简化多边形""" + poly = poly.astype(np.int32) + perimeter = cv2.arcLength(poly, True) + epsilon = epsilon_ratio * perimeter + approx = cv2.approxPolyDP(poly, epsilon, True) + return approx.reshape(-1, 2) + + +def extract_simplified_masks(result): + """ + 提取并简化 YOLO mask + 返回: [(cls_id, poly), ...] + """ + simplified = [] + + if result.masks is None: + return simplified + + boxes = result.boxes + + for i, poly in enumerate(result.masks.xy): + cls_id = int(boxes.cls[i]) + conf = float(boxes.conf[i]) + + if conf < CONF_THRES: + continue + + poly = simplify_polygon(poly, EPSILON_RATIO) + + if len(poly) < 3: + continue + + simplified.append((cls_id, poly)) + + return simplified + + +def save_yolo_seg_labels(masks, img_shape, save_path): + """保存 YOLO segmentation 标签(无目标也生成空 txt)""" + h, w = img_shape[:2] + lines = [] + + for cls_id, poly in masks: + poly_norm = [] + for x, y in poly: + poly_norm.append(f"{x / w:.6f}") + poly_norm.append(f"{y / h:.6f}") + + lines.append(str(cls_id) + " " + " ".join(poly_norm)) + + with open(save_path, "w") as f: + if lines: + f.write("\n".join(lines)) + + +def draw_polygons(img, masks): + """在图像上绘制 segmentation 多边形""" + vis = img.copy() + + for cls_id, poly in masks: + poly = poly.astype(np.int32) + + cv2.polylines( + vis, + [poly], + isClosed=True, + color=(0, 255, 0), + thickness=2 + ) + + x, y = poly[0] + cv2.putText( + vis, + str(cls_id), + (int(x), int(y)), + cv2.FONT_HERSHEY_SIMPLEX, + 0.6, + (0, 255, 0), + 2 + ) + + return vis + + +def run_folder_inference(): + # 输出目录 + out_lbl_dir = os.path.join(OUT_DIR, "labels") + out_img_dir = os.path.join(OUT_DIR, "images") + + if SAVE_LABELS: + os.makedirs(out_lbl_dir, exist_ok=True) + if SAVE_VIS: + os.makedirs(out_img_dir, exist_ok=True) + + # 加载模型(只一次) + model = YOLO(MODEL_PATH) + + img_files = sorted([ + f for f in os.listdir(IMAGE_DIR) + if f.lower().endswith(IMG_EXTS) + ]) + + print(f"📂 共检测 {len(img_files)} 张图片") + + for idx, img_name in enumerate(img_files, 1): + img_path = os.path.join(IMAGE_DIR, img_name) + img = cv2.imread(img_path) + + if img is None: + print(f"⚠️ 跳过无法读取: {img_name}") + continue + + results = model( + img, + imgsz=IMG_SIZE, + conf=CONF_THRES, + verbose=False + ) + result = results[0] + + masks = extract_simplified_masks(result) + base_name = os.path.splitext(img_name)[0] + + # ---------- 保存标签 ---------- + if SAVE_LABELS: + label_path = os.path.join(out_lbl_dir, base_name + ".txt") + save_yolo_seg_labels(masks, img.shape, label_path) + + # ---------- 保存可视化 ---------- + if SAVE_VIS: + vis_img = draw_polygons(img, masks) + vis_path = os.path.join(out_img_dir, img_name) + cv2.imwrite(vis_path, vis_img) + + print(f"[{idx}/{len(img_files)}] ✅ {img_name}") + + print("🎉 推理完成") + + +if __name__ == "__main__": + run_folder_inference() diff --git a/yolo11_seg/yolo_seg_infer_vis—f.py b/yolo11_seg/yolo_seg_infer_vis—f.py new file mode 100644 index 0000000..dcc8cf7 --- /dev/null +++ b/yolo11_seg/yolo_seg_infer_vis—f.py @@ -0,0 +1,125 @@ +import cv2 +import numpy as np +import os +from ultralytics import YOLO + +# ---------------- 配置 ---------------- +MODEL_PATH = "/home/hx/yolo/ultralytics_yolo11-main/runs/train/61seg/exp2/weights/best.pt" +IMAGE_DIR = "/media/hx/04e879fa-d697-4b02-ac7e-a4148876ebb0/dataset/cls-61/class4c/12.17-18" +OUT_DIR = "./outputs" # 只保存 labels +IMG_SIZE = 640 +CONF_THRES = 0.25 + +# 多边形简化比例(点数控制核心参数) +EPSILON_RATIO = 0.001 + +IMG_EXTS = (".jpg", ".jpeg", ".png", ".bmp") +# ------------------------------------- + + +def simplify_polygon(poly, epsilon_ratio): + """ + 使用 approxPolyDP 简化多边形 + """ + poly = poly.astype(np.int32) + perimeter = cv2.arcLength(poly, True) + epsilon = epsilon_ratio * perimeter + approx = cv2.approxPolyDP(poly, epsilon, True) + return approx.reshape(-1, 2) + + +def extract_simplified_masks(result): + """ + 提取并简化 YOLO mask + 返回: + [(cls_id, poly), ...] + """ + simplified = [] + + if result.masks is None: + return simplified + + boxes = result.boxes + + for i, poly in enumerate(result.masks.xy): + cls_id = int(boxes.cls[i]) + conf = float(boxes.conf[i]) + + if conf < CONF_THRES: + continue + + poly = simplify_polygon(poly, EPSILON_RATIO) + + if len(poly) < 3: + continue + + simplified.append((cls_id, poly)) + + return simplified + + +def save_yolo_seg_labels(masks, img_shape, save_path): + """ + 保存 YOLO segmentation 标签 + """ + h, w = img_shape[:2] + lines = [] + + for cls_id, poly in masks: + poly_norm = [] + for x, y in poly: + poly_norm.append(f"{x / w:.6f}") + poly_norm.append(f"{y / h:.6f}") + + lines.append(str(cls_id) + " " + " ".join(poly_norm)) + + # ⚠️ 没有目标也生成空 txt(YOLO 训练需要) + with open(save_path, "w") as f: + if lines: + f.write("\n".join(lines)) + + +def run_folder_inference(): + out_lbl_dir = os.path.join(OUT_DIR, "labels") + os.makedirs(out_lbl_dir, exist_ok=True) + + # 模型只加载一次 + model = YOLO(MODEL_PATH) + + img_files = sorted([ + f for f in os.listdir(IMAGE_DIR) + if f.lower().endswith(IMG_EXTS) + ]) + + print(f"📂 共检测 {len(img_files)} 张图片") + + for idx, img_name in enumerate(img_files, 1): + img_path = os.path.join(IMAGE_DIR, img_name) + img = cv2.imread(img_path) + + if img is None: + print(f"⚠️ 跳过无法读取: {img_name}") + continue + + results = model( + img, + imgsz=IMG_SIZE, + conf=CONF_THRES, + verbose=False + ) + result = results[0] + + masks = extract_simplified_masks(result) + + base_name = os.path.splitext(img_name)[0] + label_path = os.path.join(out_lbl_dir, base_name + ".txt") + + save_yolo_seg_labels(masks, img.shape, label_path) + + print(f"[{idx}/{len(img_files)}] ✅ {img_name}") + + print("🎉 标签生成完成(仅保存 YOLO Seg 标签)") + + +if __name__ == "__main__": + run_folder_inference() diff --git a/zhuangtai_class_cls/test_2cls_file.py b/zhuangtai_class_cls/1test_2cls_file.py similarity index 84% rename from zhuangtai_class_cls/test_2cls_file.py rename to zhuangtai_class_cls/1test_2cls_file.py index 37d30eb..8ec36d2 100644 --- a/zhuangtai_class_cls/test_2cls_file.py +++ b/zhuangtai_class_cls/1test_2cls_file.py @@ -35,7 +35,7 @@ def classify_and_save_images(model_path, input_folder, output_root): # 复制图片到对应类别文件夹 dst_path = class_dirs[class_id] / img_path.name - shutil.copy2(img_path, dst_path) + shutil.move(img_path, dst_path) print(f"Processed {img_path.name} -> Class {class_id}") @@ -45,11 +45,11 @@ def classify_and_save_images(model_path, input_folder, output_root): if __name__ == "__main__": # 配置路径 - model_path = r'gaiban.pt' # 或直接使用训练好的权重路径如 'runs/train/cls/exp_cls/weights/best.pt' + model_path = r'/home/hx/yolo/ultralytics_yolo11-main/runs/train/cls/exp_zdb_cls2/weights/best.pt' # 或直接使用训练好的权重路径如 'runs/train/cls/exp_cls/weights/best.pt' #input_folder = '/media/hx/04e879fa-d697-4b02-ac7e-a4148876ebb0/dataset/datalodad/f13' # 替换为你的测试图片文件夹路径 #output_root = '/media/hx/04e879fa-d697-4b02-ac7e-a4148876ebb0/dataset/datalodad' # 输出根目录 - input_folder = '/media/hx/04e879fa-d697-4b02-ac7e-a4148876ebb0/dataset/f6' # 替换为你的测试图片文件夹路径 - output_root = '/media/hx/04e879fa-d697-4b02-ac7e-a4148876ebb0/dataset/class11' # 输出根目录 + input_folder = '/media/hx/04e879fa-d697-4b02-ac7e-a4148876ebb0/dataset/1/class/class0' # 替换为你的测试图片文件夹路径 + output_root = '/media/hx/04e879fa-d697-4b02-ac7e-a4148876ebb0/dataset/1/class/class' # 输出根目录 # 执行分类 classify_and_save_images(model_path, input_folder, output_root) \ No newline at end of file diff --git a/zhuangtai_class_cls/22.png b/zhuangtai_class_cls/22.png new file mode 100644 index 0000000..01f7099 Binary files /dev/null and b/zhuangtai_class_cls/22.png differ diff --git a/zhuangtai_class_cls/5.png b/zhuangtai_class_cls/5.png new file mode 100644 index 0000000..36d4653 Binary files /dev/null and b/zhuangtai_class_cls/5.png differ diff --git a/zhuangtai_class_cls/chose_ROI.py b/zhuangtai_class_cls/chose_ROI.py index 5d62a57..4f98393 100644 --- a/zhuangtai_class_cls/chose_ROI.py +++ b/zhuangtai_class_cls/chose_ROI.py @@ -7,7 +7,7 @@ drawing = False # 是否正在绘制 ix, iy = -1, -1 # 起始点 roi_list = [] # 存储多个 ROI 坐标 [(x, y, w, h), ...] image_path = "3.png" # <<< 修改为你自己的图像路径 -save_dir = "./roi_111/2.txt" # 保存坐标的目录 +save_dir = "./roi_111/13.3.txt" # 保存坐标的目录 # 创建保存目录 os.makedirs(save_dir, exist_ok=True) diff --git a/zhuangtai_class_cls/lunkuoxian_f.py b/zhuangtai_class_cls/lunkuoxian_f.py new file mode 100644 index 0000000..a48111f --- /dev/null +++ b/zhuangtai_class_cls/lunkuoxian_f.py @@ -0,0 +1,88 @@ +import cv2 +import numpy as np +from sklearn.linear_model import RANSACRegressor +from scipy.interpolate import UnivariateSpline +from pathlib import Path +import os + +# ----------------------------- +# 配置 +# ----------------------------- +INPUT_FOLDER = "/home/hx/yolo/yemian/test_image" # 输入图片文件夹 +OUTPUT_FOLDER = "/home/hx/yolo/yemian/test_image/output" # 保存结果 +ROI = (519, 757, 785, 50) # x, y, w, h +RANSAC_RES_THRESHOLD = 2 # RANSAC residual_threshold +SPLINE_S = 5 # spline 平滑系数 + +os.makedirs(OUTPUT_FOLDER, exist_ok=True) + +# ----------------------------- +# 批量处理 +# ----------------------------- +image_files = list(Path(INPUT_FOLDER).glob("*.png")) + list(Path(INPUT_FOLDER).glob("*.jpg")) + +for img_path in image_files: + print(f"处理图片: {img_path.name}") + img = cv2.imread(str(img_path)) + if img is None: + print("无法读取图片,跳过") + continue + + x, y, w, h = ROI + roi_img = img[y:y+h, x:x+w].copy() + + # 灰度 + CLAHE + 高斯模糊 + gray = cv2.cvtColor(roi_img, cv2.COLOR_BGR2GRAY) + clahe = cv2.createCLAHE(clipLimit=2.0, tileGridSize=(8,8)) + gray_clahe = clahe.apply(gray) + blur = cv2.GaussianBlur(gray_clahe, (3,3), 0) + + # Canny 边缘 + edges = cv2.Canny(blur, 50, 150) + + # 提取边缘点 + ys, xs = np.where(edges > 0) + points = np.vstack([xs, ys]).T # shape=(N,2) + + if len(points) < 10: + print("边缘点太少,跳过") + continue + + # ----------------------------- + # RANSAC 拟合 y = f(x) + # ----------------------------- + x_pts = points[:,0].reshape(-1,1) + y_pts = points[:,1] + + try: + ransac = RANSACRegressor(min_samples=5, residual_threshold=RANSAC_RES_THRESHOLD, max_trials=1000) + ransac.fit(x_pts, y_pts) + y_ransac = ransac.predict(x_pts) + + # spline 平滑 + sort_idx = np.argsort(x_pts[:,0]) + x_sorted = x_pts[:,0][sort_idx] + y_sorted = y_ransac[sort_idx] + spline = UnivariateSpline(x_sorted, y_sorted, s=SPLINE_S) + x_new = np.linspace(x_sorted[0], x_sorted[-1], 500) + y_new = spline(x_new) + y_new = np.nan_to_num(y_new, nan=np.mean(y_sorted)) + + except Exception as e: + print(f"红色曲线拟合失败,跳过: {e}") + continue + + # ----------------------------- + # 可视化红色曲线 + # ----------------------------- + output = roi_img.copy() + for i in range(len(x_new)-1): + cv2.line(output, + (int(x_new[i]), int(y_new[i])), + (int(x_new[i+1]), int(y_new[i+1])), + (0,0,255), 2) + + # 保存结果 + out_path = Path(OUTPUT_FOLDER) / f"{img_path.stem}_curve.png" + cv2.imwrite(str(out_path), output) + print(f"保存可视化结果: {out_path.name}") diff --git a/zhuangtai_class_cls/resize_dataset_image61.py b/zhuangtai_class_cls/resize_dataset_image61.py index f216c3d..20a450f 100644 --- a/zhuangtai_class_cls/resize_dataset_image61.py +++ b/zhuangtai_class_cls/resize_dataset_image61.py @@ -4,8 +4,8 @@ import cv2 # ---------------------------- # 配置 # ---------------------------- -SOURCE_ROOT_DIR = "/media/hx/04e879fa-d697-4b02-ac7e-a4148876ebb0/dataset/cls-new/19c" # 原始图片根目录 -TARGET_ROOT_DIR = "/media/hx/04e879fa-d697-4b02-ac7e-a4148876ebb0/dataset/cls-new/19cc" # 输出根目录 +SOURCE_ROOT_DIR = "/media/hx/04e879fa-d697-4b02-ac7e-a4148876ebb0/dataset/cls-61/19c" # 原始图片根目录 +TARGET_ROOT_DIR = "/media/hx/04e879fa-d697-4b02-ac7e-a4148876ebb0/dataset/cls-61/19cc" # 输出根目录 CLASSES = ["class0", "class1", "class2", "class3", "class4"] # 类别列表 TARGET_SIZE = 640 # resize 尺寸 SUBSETS = ["train", "val", "test"] diff --git a/zhuangtai_class_cls/resize—renyi.py b/zhuangtai_class_cls/resize—renyi.py new file mode 100644 index 0000000..ca2e20e --- /dev/null +++ b/zhuangtai_class_cls/resize—renyi.py @@ -0,0 +1,74 @@ +import os +import cv2 + +# ---------------------------- +# 配置 +# ---------------------------- +SOURCE_ROOT_DIR = "/media/hx/04e879fa-d697-4b02-ac7e-a4148876ebb0/dataset/cls-61/class4" +TARGET_ROOT_DIR = "/media/hx/04e879fa-d697-4b02-ac7e-a4148876ebb0/dataset/cls-61/class4c" +TARGET_SIZE = 640 # resize 尺寸 + +# ---------------------------- +# 全局 ROI (x, y, w, h) +# ---------------------------- +GLOBAL_ROI = [445, 540, 931, 319] +#GLOBAL_ROI = [604, 182, 594, 252] + + +# ---------------------------- +# 主处理函数 +# ---------------------------- +def process_images(): + x, y, w, h = GLOBAL_ROI + + # 遍历主目录下的所有子文件夹 + for sub_dir_name in sorted(os.listdir(SOURCE_ROOT_DIR)): + src_sub_dir = os.path.join(SOURCE_ROOT_DIR, sub_dir_name) + + if not os.path.isdir(src_sub_dir): + continue # 跳过非文件夹 + + tgt_sub_dir = os.path.join(TARGET_ROOT_DIR, sub_dir_name) + os.makedirs(tgt_sub_dir, exist_ok=True) + + print(f"\n📁 处理文件夹: {sub_dir_name}") + + for file in os.listdir(src_sub_dir): + if not file.lower().endswith((".jpg", ".png", ".jpeg", ".bmp", ".tif", ".tiff")): + continue + + img_path = os.path.join(src_sub_dir, file) + img = cv2.imread(img_path) + + if img is None: + print(f"❌ 无法读取图片: {img_path}") + continue + + h_img, w_img = img.shape[:2] + + # ROI 边界保护 + x1 = max(0, x) + y1 = max(0, y) + x2 = min(w_img, x + w) + y2 = min(h_img, y + h) + + cropped = img[y1:y2, x1:x2] + if cropped.size == 0: + print(f"❌ 裁剪结果为空: {img_path}") + continue + + resized = cv2.resize(cropped, (TARGET_SIZE, TARGET_SIZE), interpolation=cv2.INTER_AREA) + + tgt_path = os.path.join(tgt_sub_dir, file) + cv2.imwrite(tgt_path, resized) + + print(f"✅ {sub_dir_name}/{file}") + + print("\n🎉 全部图片处理完成") + + +# ---------------------------- +# 入口 +# ---------------------------- +if __name__ == "__main__": + process_images() diff --git a/zhuangtai_class_cls/tuili_f_move—60.py b/zhuangtai_class_cls/tuili_f_move—60.py index f3eebf7..8d037c3 100644 --- a/zhuangtai_class_cls/tuili_f_move—60.py +++ b/zhuangtai_class_cls/tuili_f_move—60.py @@ -122,15 +122,15 @@ def batch_classify_images(model_path, input_folder, output_root, roi_file, targe continue try: - print(f"\n📄 处理: {img_path.name}") + print(f"\n处理: {img_path.name}") img = cv2.imread(str(img_path)) if img is None: - print(f"❌ 无法读取图像: {img_path.name}") + print(f"无法读取图像: {img_path.name}") continue crops = crop_and_resize(img, rois, target_size) if not crops: - print(f"⚠️ 无有效 ROI 裁剪区域: {img_path.name}") + print(f"无有效 ROI 裁剪区域: {img_path.name}") continue detected_classes = [] @@ -138,7 +138,7 @@ def batch_classify_images(model_path, input_folder, output_root, roi_file, targe 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})") + print(f" ROI{roi_idx}: {final_class} (score={score:.2f})") most_severe_class = min(detected_classes, key=lambda x: severity_rank.get(x, 99)) @@ -147,7 +147,7 @@ def batch_classify_images(model_path, input_folder, output_root, roi_file, targe # ----------------------------- dst_path = class_dirs[most_severe_class] / img_path.name shutil.move(str(img_path), str(dst_path)) - print(f"📦 已移动 -> [{most_severe_class}] {dst_path}") + print(f"已移动 -> [{most_severe_class}] {dst_path}") processed_count += 1 @@ -160,9 +160,9 @@ 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_cls11/weights/best.pt" - input_folder = "/media/hx/04e879fa-d697-4b02-ac7e-a4148876ebb0/dataset/1/111/1" - output_root = "/media/hx/04e879fa-d697-4b02-ac7e-a4148876ebb0/dataset/1/111/2" + model_path = "/home/hx/yolo/ultralytics_yolo11-main/runs/train/cls_resize/exp_cls13_new/weights/best.pt" + input_folder = "/media/hx/04e879fa-d697-4b02-ac7e-a4148876ebb0/dataset/1/1226c01" + output_root = "/media/hx/04e879fa-d697-4b02-ac7e-a4148876ebb0/dataset/1/1226c01" roi_file = "./roi_coordinates/3.txt" target_size = 640 threshold = 0.4 diff --git a/zhuangtai_class_cls/tuili_f_yuantusave61.py b/zhuangtai_class_cls/tuili_f_yuantusave61.py index 887376f..e9dd99c 100644 --- a/zhuangtai_class_cls/tuili_f_yuantusave61.py +++ b/zhuangtai_class_cls/tuili_f_yuantusave61.py @@ -1,4 +1,5 @@ import os +import shutil from pathlib import Path import cv2 import numpy as np @@ -34,7 +35,6 @@ def load_global_rois(txt_path): print(f"⚠️ 无法解析 ROI 行 '{s}': {e}") return rois - # --------------------------- # 裁剪并 resize ROI # --------------------------- @@ -42,7 +42,6 @@ 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 @@ -51,7 +50,6 @@ def crop_and_resize(img, rois, target_size=640): crops.append((roi_resized, i)) return crops - # --------------------------- # class1/class2 加权判断 # --------------------------- @@ -66,7 +64,6 @@ def weighted_small_large(pred_probs, threshold=0.4, w1=0.3, w2=0.7): final_class = "大堆料" if score >= threshold else "小堆料" return final_class, score, p1, p2 - # --------------------------- # 单张图片推理函数 # --------------------------- @@ -88,16 +85,15 @@ def classify_image_weighted(image, model, threshold=0.5): return final_class, score, p1, p2 - # --------------------------- -# 批量推理主函数(保存原图,不改名) +# 批量推理主函数(移动文件) # --------------------------- -def batch_classify_images(model_path, input_folder, output_root, roi_file, target_size=640, threshold=0.5): +def batch_classify_images_move(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}") @@ -126,12 +122,12 @@ def batch_classify_images(model_path, input_folder, output_root, roi_file, targe "浇筑满": 4 } - # 遍历输入文件夹中的所有图片 + # 遍历输入文件夹 input_folder = Path(input_folder) image_extensions = {'.jpg', '.jpeg', '.png', '.bmp', '.tif', '.tiff'} - processed_count = 0 - for img_path in input_folder.glob("*.*"): + + for img_path in sorted(input_folder.glob("*.*")): if img_path.suffix.lower() not in image_extensions: continue @@ -144,55 +140,49 @@ def batch_classify_images(model_path, input_folder, output_root, roi_file, targe # 裁剪并缩放 ROI crops = crop_and_resize(img, rois, target_size) - if not crops: print(f"⚠️ 无有效 ROI 裁剪区域: {img_path.name}") continue detected_classes = [] - # 遍历每个 ROI 进行分类 + # 遍历每个 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}") + # 移动文件 + shutil.move(str(img_path), str(dst_path)) + 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} 张图像。") + print(f"\n🎉 批量处理完成!共移动 {processed_count} 张图像。") # --------------------------- -# 使用示例(请根据实际情况修改路径) +# 使用示例 # --------------------------- if __name__ == "__main__": - # 🔧 用户配置区 model_path = "/home/hx/yolo/ultralytics_yolo11-main/runs/train/cls_resize1/exp_cls2/weights/best.pt" - input_folder = "/media/hx/04e879fa-d697-4b02-ac7e-a4148876ebb0/dataset/1/12.2" - output_root = "/media/hx/04e879fa-d697-4b02-ac7e-a4148876ebb0/dataset/1/12.2.1" + input_folder = "/media/hx/04e879fa-d697-4b02-ac7e-a4148876ebb0/dataset/1/61" + output_root = "/media/hx/04e879fa-d697-4b02-ac7e-a4148876ebb0/dataset/1/61" roi_file = "./roi_coordinates/2.txt" - target_size = 640 - threshold = 0.4 # 小堆料 vs 大堆料的加权阈值 + threshold = 0.4 - # 🚀 开始执行 - batch_classify_images( + batch_classify_images_move( model_path=model_path, input_folder=input_folder, output_root=output_root, roi_file=roi_file, - target_size=target_size, + target_size=640, threshold=threshold - ) \ No newline at end of file + ) diff --git a/zhuangtai_class_cls/tuili_f_yuantusave61_nojiaquan.py b/zhuangtai_class_cls/tuili_f_yuantusave61_nojiaquan.py new file mode 100644 index 0000000..d295c7c --- /dev/null +++ b/zhuangtai_class_cls/tuili_f_yuantusave61_nojiaquan.py @@ -0,0 +1,170 @@ +import os +import shutil +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 + +# --------------------------- +# 单张图片分类(无加权) +# --------------------------- +def classify_image(image, model): + results = model(image) + probs = results[0].probs.data.cpu().numpy().flatten() + + class_id = int(probs.argmax()) + confidence = float(probs[class_id]) + class_name = CLASS_NAMES.get(class_id, f"未知类别({class_id})") + + return class_name, confidence + +# --------------------------- +# 批量推理主函数(移动文件) +# --------------------------- +def batch_classify_images_move( + model_path, + input_folder, + output_root, + roi_file, + target_size=640 +): + 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_exts = {'.jpg', '.jpeg', '.png', '.bmp', '.tif', '.tiff'} + processed = 0 + + for img_path in sorted(input_folder.glob("*.*")): + if img_path.suffix.lower() not in image_exts: + continue + + try: + print(f"\n📄 处理: {img_path.name}") + img = cv2.imread(str(img_path)) + if img is None: + print("❌ 读取失败") + continue + + crops = crop_and_resize(img, rois, target_size) + if not crops: + print("⚠️ 无有效 ROI") + continue + + detected_classes = [] + + for roi_img, roi_idx in crops: + cls, conf = classify_image(roi_img, model) + detected_classes.append(cls) + print(f" 🔍 ROI{roi_idx}: {cls} ({conf:.2f})") + + # 取最严重结果 + final_class = min( + detected_classes, + key=lambda x: severity_rank.get(x, 99) + ) + + dst = class_dirs[final_class] / img_path.name + shutil.move(str(img_path), str(dst)) + print(f"✅ 移动 -> [{final_class}]") + + processed += 1 + + except Exception as e: + print(f"❌ 处理失败 {img_path.name}: {e}") + + print(f"\n🎉 完成,共处理 {processed} 张") + +# --------------------------- +# 使用示例 +# --------------------------- +if __name__ == "__main__": + model_path = "/home/hx/yolo/ultralytics_yolo11-main/runs/train/cls_resize1/exp_cls2/weights/best.pt" + input_folder = "/media/hx/04e879fa-d697-4b02-ac7e-a4148876ebb0/dataset/camera02/ready" + output_root = "/media/hx/04e879fa-d697-4b02-ac7e-a4148876ebb0/dataset/camera02/ready" + roi_file = "./roi_coordinates/2.txt" + threshold = 0.4 + + batch_classify_images_move( + model_path=model_path, + input_folder=input_folder, + output_root=output_root, + roi_file=roi_file, + target_size=640 + ) diff --git a/zhuangtai_class_cls/tuili_f_yuantusave—2cls.py b/zhuangtai_class_cls/tuili_f_yuantusave—2cls.py index a6d1426..575ab82 100644 --- a/zhuangtai_class_cls/tuili_f_yuantusave—2cls.py +++ b/zhuangtai_class_cls/tuili_f_yuantusave—2cls.py @@ -7,12 +7,12 @@ from ultralytics import YOLO # --------------------------- # 配置路径(请按需修改) # --------------------------- -MODEL_PATH = "xialiao.pt" # 你的二分类模型 -INPUT_FOLDER = "/media/hx/04e879fa-d697-4b02-ac7e-a4148876ebb0/dataset/1/ready" # 输入图像文件夹 -OUTPUT_ROOT = "/media/hx/04e879fa-d697-4b02-ac7e-a4148876ebb0/dataset/1/ready/result" # 输出根目录 +MODEL_PATH = "gaiban.pt" # 你的二分类模型 +INPUT_FOLDER = "/media/hx/04e879fa-d697-4b02-ac7e-a4148876ebb0/dataset/camera02" # 输入图像文件夹 +OUTPUT_ROOT = "/media/hx/04e879fa-d697-4b02-ac7e-a4148876ebb0/dataset/camera02" # 输出根目录 # 类别映射(必须与训练时的 data.yaml 顺序一致) -CLASS_NAMES = {0: "不合格", 1: "合格"} +CLASS_NAMES = {0: "noready", 1: "ready"} # --------------------------- # 批量推理函数(移动原图) diff --git a/zhuangtai_class_cls/轮廓线.py b/zhuangtai_class_cls/轮廓线.py new file mode 100644 index 0000000..2c817c5 --- /dev/null +++ b/zhuangtai_class_cls/轮廓线.py @@ -0,0 +1,104 @@ +import cv2 +import numpy as np + +# ======================== +# 配置 +# ======================== +IMAGE_PATH = "22.png" + +CANNY_LOW = 60 +CANNY_HIGH = 150 + +# 判定“近似水平”的阈值 +MAX_SLOPE = 0.2 # |dy/dx| < 0.2 认为是水平 + +# ======================== + + +def main(): + img = cv2.imread(IMAGE_PATH) + if img is None: + raise RuntimeError("图片读取失败") + + h, w = img.shape[:2] + + # 1. 灰度 + gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) + + # 2. 模糊(非常关键,压制粗糙纹理) + blur = cv2.GaussianBlur(gray, (7, 7), 0) + + # 3. Canny + edges = cv2.Canny(blur, CANNY_LOW, CANNY_HIGH) + + # 4. 找轮廓(只用来拿点) + contours, _ = cv2.findContours( + edges, cv2.RETR_LIST, cv2.CHAIN_APPROX_NONE + ) + + horizontal_points = [] + + # 5. 筛选“近似水平”的边缘点 + for cnt in contours: + if len(cnt) < 50: + continue + + pts = cnt.squeeze() + if pts.ndim != 2: + continue + + x = pts[:, 0] + y = pts[:, 1] + + dx = x.max() - x.min() + dy = y.max() - y.min() + + if dx < 100: # 太短的不要 + continue + + slope = dy / (dx + 1e-6) + + if slope < MAX_SLOPE: + horizontal_points.append(pts) + + if not horizontal_points: + print("❌ 没找到合适的水平边") + return + + # 6. 合并所有候选点 + all_pts = np.vstack(horizontal_points) + + # 7. 选“最靠上的那一条” + # 方法:按 y 排序,取 y 最小的一部分 + all_pts = all_pts[all_pts[:, 1].argsort()] + + # 取前 20% 作为“上沿候选” + top_n = int(len(all_pts) * 0.2) + top_edge_pts = all_pts[:top_n] + + # 8. 用最小二乘拟合直线 y = ax + b + xs = top_edge_pts[:, 0] + ys = top_edge_pts[:, 1] + + a, b = np.polyfit(xs, ys, 1) + + # 9. 画线 + x0, x1 = 0, w - 1 + y0 = int(a * x0 + b) + y1 = int(a * x1 + b) + + vis = img.copy() + cv2.line(vis, (x0, y0), (x1, y1), (0, 0, 255), 2) + + # 可视化点 + for p in top_edge_pts[::20]: + cv2.circle(vis, tuple(p), 1, (0, 255, 0), -1) + + cv2.imshow("edges", edges) + cv2.imshow("top plate edge", vis) + cv2.waitKey(0) + cv2.destroyAllWindows() + + +if __name__ == "__main__": + main() diff --git a/zjsh_code/muju_cls/train/3cls.yaml b/zjsh_code/muju_cls/train/3cls.yaml new file mode 100644 index 0000000..51ccdba --- /dev/null +++ b/zjsh_code/muju_cls/train/3cls.yaml @@ -0,0 +1,30 @@ +# Ultralytics YOLO 🚀, AGPL-3.0 license +# YOLO11-cls image classification model. For Usage examples see https://docs.ultralytics.com/tasks/classify + +# Parameters +nc: 3 # number of classes +scales: # model compound scaling constants, i.e. 'model=yolo11n-cls.yaml' will call yolo11-cls.yaml with scale 'n' + # [depth, width, max_channels] + n: [0.50, 0.25, 1024] # summary: 151 layers, 1633584 parameters, 1633584 gradients, 3.3 GFLOPs + s: [0.50, 0.50, 1024] # summary: 151 layers, 5545488 parameters, 5545488 gradients, 12.2 GFLOPs + m: [0.50, 1.00, 512] # summary: 187 layers, 10455696 parameters, 10455696 gradients, 39.7 GFLOPs + l: [1.00, 1.00, 512] # summary: 309 layers, 12937104 parameters, 12937104 gradients, 49.9 GFLOPs + x: [1.00, 1.50, 512] # summary: 309 layers, 28458544 parameters, 28458544 gradients, 111.1 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, 2, C2PSA, [1024]] # 9 + +# YOLO11n head +head: + - [-1, 1, Classify, [nc]] # Classify diff --git a/zjsh_code/muju_cls/train/train_3cls_main.py b/zjsh_code/muju_cls/train/train_3cls_main.py new file mode 100644 index 0000000..64ab902 --- /dev/null +++ b/zjsh_code/muju_cls/train/train_3cls_main.py @@ -0,0 +1,18 @@ +from ultralytics import YOLO + +if __name__ == '__main__': + model = YOLO(r'3cls.yaml') + results = model.train( + data='/home/dy/dataset/muju', + epochs=300, + imgsz=640, + batch=4, + workers=5, + device='0', + project='runs/train/muju_cls', + name='exp_cls', + exist_ok=False, + optimizer='AdamW', + lr0=0.0005, + patience=0, + ) diff --git a/zjsh_code/muju_cls/val/main_pc.py b/zjsh_code/muju_cls/val/main_pc.py new file mode 100644 index 0000000..1f7d7b4 --- /dev/null +++ b/zjsh_code/muju_cls/val/main_pc.py @@ -0,0 +1,73 @@ +import cv2 +from ultralytics import YOLO + +# --------------------------- +# 配置路径(请按需修改) +# --------------------------- +MODEL_PATH = "muju.pt" +IMAGE_PATH = "./test_img/class0.png" # 替换为你要测试的单张图片路径 + +# 类别映射:必须与训练时 data.yaml 的 names 顺序一致 +CLASS_NAMES = { + 0: "模具车非f块", + 1: "模具车f块", + 2: "有遮挡" +} + + +# --------------------------- +# 单张图片推理函数 +# --------------------------- +def classify_single_image(model_path, image_path, class_names): + # 加载模型 + model = YOLO(model_path) + print(f"模型加载成功: {model_path}") + + # 读取图像 + img = cv2.imread(image_path) + if img is None: + print(f"无法读取图像: {image_path}") + return + + print(f"📷 正在推理: {image_path}") + + # 推理 + results = model(img) + probs = results[0].probs.data.cpu().numpy() + pred_class_id = int(probs.argmax()) + pred_label = class_names[pred_class_id] + confidence = float(probs[pred_class_id]) + + # 输出结果 + print("\n" + "=" * 40) + print(f"🔍 预测结果:") + print(f" 类别: {pred_label}") + print(f" 置信度: {confidence:.4f}") + print(f" 类别ID: {pred_class_id}") + print("=" * 40) + + # (可选)在图像上显示结果并保存/显示 + # 这里我们只打印,不保存。如需可视化,取消下面注释: + """ + label_text = f"{pred_label} ({confidence:.2f})" + cv2.putText(img, label_text, (10, 30), cv2.FONT_HERSHEY_SIMPLEX, 1.0, (0, 255, 0), 2) + cv2.imshow("Result", img) + cv2.waitKey(0) + cv2.destroyAllWindows() + + # 或保存带标签的图 + # output_img_path = image_path.replace(".jpg", "_result.jpg") + # cv2.imwrite(output_img_path, img) + # print(f"带标签图像已保存: {output_img_path}") + """ + + +# --------------------------- +# 运行入口 +# --------------------------- +if __name__ == "__main__": + classify_single_image( + model_path=MODEL_PATH, + image_path=IMAGE_PATH, + class_names=CLASS_NAMES + ) \ No newline at end of file diff --git a/zjsh_code/muju_cls/val/muju.pt b/zjsh_code/muju_cls/val/muju.pt new file mode 100644 index 0000000..8d7e09f Binary files /dev/null and b/zjsh_code/muju_cls/val/muju.pt differ diff --git a/zjsh_code/muju_cls/val/test_img/class1.png b/zjsh_code/muju_cls/val/test_img/class1.png new file mode 100644 index 0000000..98eba5a Binary files /dev/null and b/zjsh_code/muju_cls/val/test_img/class1.png differ diff --git a/zjsh_code/obb_main/train/obb_data.yaml b/zjsh_code/obb_main/train/obb_data.yaml new file mode 100644 index 0000000..7455581 --- /dev/null +++ b/zjsh_code/obb_main/train/obb_data.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: ./dataset +train: train # 数据集路径下的train.txt +val: val # 数据集路径下的val.txt +test: test # 数据集路径下的test.txt + +nc: 1 +names: ['clamp'] + diff --git a/zjsh_code/obb_main/train/train_obb_zengqiang.py b/zjsh_code/obb_main/train/train_obb_zengqiang.py new file mode 100644 index 0000000..08bbf61 --- /dev/null +++ b/zjsh_code/obb_main/train/train_obb_zengqiang.py @@ -0,0 +1,42 @@ +from ultralytics import YOLO + +if __name__ == '__main__': + # ✅ 推荐:加载官方预训练模型 或 自己的 best.pt + model = YOLO(r'yolo11-obb.yaml') # + + results = model.train( + data='obb_data.yaml', + epochs=500, # 减少 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, # 每轮验证 + + # + amp=False, + ) \ No newline at end of file diff --git a/zjsh_code/obb_main/train/yolo11-obb.yaml b/zjsh_code/obb_main/train/yolo11-obb.yaml new file mode 100644 index 0000000..65d9b8a --- /dev/null +++ b/zjsh_code/obb_main/train/yolo11-obb.yaml @@ -0,0 +1,47 @@ +# Ultralytics YOLO 🚀, AGPL-3.0 license +# YOLO11 Oriented Bounding Boxes (OBB) model with P3-P5 outputs. For Usage examples see https://docs.ultralytics.com/tasks/obb + +# Parameters +nc: 1 # number of classes +scales: # model compound scaling constants, i.e. 'model=yolo11n-obb.yaml' will call yolo11-obb.yaml with scale 'n' + # [depth, width, max_channels] + n: [0.50, 0.25, 1024] # summary: 344 layers, 2695747 parameters, 2695731 gradients, 6.9 GFLOPs + s: [0.50, 0.50, 1024] # summary: 344 layers, 9744931 parameters, 9744915 gradients, 22.7 GFLOPs + m: [0.50, 1.00, 512] # summary: 434 layers, 20963523 parameters, 20963507 gradients, 72.2 GFLOPs + l: [1.00, 1.00, 512] # summary: 656 layers, 26220995 parameters, 26220979 gradients, 91.3 GFLOPs + x: [1.00, 1.50, 512] # summary: 656 layers, 58875331 parameters, 58875315 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, OBB, [nc, 1]] # Detect(P3, P4, P5) diff --git a/推理图片反向上传CVAT/detect/best.pt b/推理图片反向上传CVAT/detect/best.pt new file mode 100644 index 0000000..db2202a Binary files /dev/null and b/推理图片反向上传CVAT/detect/best.pt differ diff --git a/推理图片反向上传CVAT/detect/trans_obbtocvat.py b/推理图片反向上传CVAT/detect/trans_obbtocvat.py index 9f439d1..9409072 100644 --- a/推理图片反向上传CVAT/detect/trans_obbtocvat.py +++ b/推理图片反向上传CVAT/detect/trans_obbtocvat.py @@ -140,13 +140,13 @@ def yolo_detect_to_cvat_xml(label_dir, image_dir, class_id_to_name, output_xml): # ------------------- 主函数 ------------------- if __name__ == "__main__": CLASS_MAP = { - 0: "hole", - 1: "crack" + 0: "bag", + 1: "bag35" } yolo_detect_to_cvat_xml( label_dir="/home/hx/yolo/推理图片反向上传CVAT/detect/inference_results/labels", - image_dir="/home/hx/开发/ML_xiantiao/class_xiantiao_pc/test_image/train", + image_dir="/media/hx/04e879fa-d697-4b02-ac7e-a4148876ebb0/dataset/ailaidete/train/delet", class_id_to_name=CLASS_MAP, output_xml="detect_annotations.xml" ) \ No newline at end of file diff --git a/推理图片反向上传CVAT/detect/tuili_save_txt_f.py b/推理图片反向上传CVAT/detect/tuili_save_txt_f.py index 328613e..7e659af 100644 --- a/推理图片反向上传CVAT/detect/tuili_save_txt_f.py +++ b/推理图片反向上传CVAT/detect/tuili_save_txt_f.py @@ -123,8 +123,8 @@ def save_yolo_detect_labels_from_folder( # ------------------- 主函数调用 ------------------- if __name__ == "__main__": - MODEL_PATH = "/home/hx/yolo/ultralytics_yolo11-main/runs/train/exp_detect/weights/best.pt" - IMAGE_DIR = "/home/hx/开发/ML_xiantiao/class_xiantiao_pc/test_image/train" + MODEL_PATH = "/home/hx/yolo/ultralytics_yolo11-main/runs/train/exp_ailai_detect3/weights/best.pt" + IMAGE_DIR = "/media/hx/04e879fa-d697-4b02-ac7e-a4148876ebb0/dataset/ailaidete/train/delet" OUTPUT_DIR = "./inference_results" save_yolo_detect_labels_from_folder( diff --git a/推理图片反向上传CVAT/point/point.pt b/推理图片反向上传CVAT/point/point.pt new file mode 100644 index 0000000..dc47fcc Binary files /dev/null and b/推理图片反向上传CVAT/point/point.pt differ diff --git a/推理图片反向上传CVAT/point/point_test.py b/推理图片反向上传CVAT/point/point_test.py index c96c005..cfbfea8 100644 --- a/推理图片反向上传CVAT/point/point_test.py +++ b/推理图片反向上传CVAT/point/point_test.py @@ -4,8 +4,8 @@ from ultralytics import YOLO import os # ====================== 用户配置 ====================== -MODEL_PATH = 'point.pt' -IMAGE_SOURCE_DIR = '/media/hx/04e879fa-d697-4b02-ac7e-a4148876ebb0/dataset/1/20251212' +MODEL_PATH = 'best.pt' +IMAGE_SOURCE_DIR = '/media/hx/04e879fa-d697-4b02-ac7e-a4148876ebb0/dataset/1/20251230' OUTPUT_DIR = './keypoints_txt' IMG_EXTENSIONS = {'.png', '.jpg', '.jpeg', '.bmp', '.tiff', '.tif', '.webp'} diff --git a/推理图片反向上传CVAT/point/trans_pointtocvat.py b/推理图片反向上传CVAT/point/trans_pointtocvat.py index 2ced821..ba9af1b 100644 --- a/推理图片反向上传CVAT/point/trans_pointtocvat.py +++ b/推理图片反向上传CVAT/point/trans_pointtocvat.py @@ -11,7 +11,7 @@ labels_dir = "keypoints_txt" output_xml = "annotations_cvat.xml" # 图片目录(用于 width/height) -images_dir = "/media/hx/04e879fa-d697-4b02-ac7e-a4148876ebb0/dataset/1/20251212" +images_dir = "/media/hx/04e879fa-d697-4b02-ac7e-a4148876ebb0/dataset/1/20251230" # 类别映射 class_mapping_reverse = { diff --git a/推理图片反向上传CVAT/seg/annotations.xml b/推理图片反向上传CVAT/seg/annotations.xml new file mode 100644 index 0000000..31d07d8 --- /dev/null +++ b/推理图片反向上传CVAT/seg/annotations.xml @@ -0,0 +1,5613 @@ + + + 1.1 + + + 297 + zj_seg_61_0113_1 + 1389 + annotation + 0 + + 2026-01-14 02:14:37.355029+00:00 + 2026-01-14 05:23:50.591580+00:00 + default + 0 + 1388 + + + + 208 + 0 + 1388 + http://www.xj-robot.com:9000/api/jobs/208 + + + + huangxin + 2193534909@qq.com + + + + + + + + + 2026-01-14 07:22:51.072604+00:00 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/推理图片反向上传CVAT/seg/del.py b/推理图片反向上传CVAT/seg/del.py new file mode 100644 index 0000000..a6111f9 --- /dev/null +++ b/推理图片反向上传CVAT/seg/del.py @@ -0,0 +1,12 @@ +import xml.etree.ElementTree as ET + +# 读取 XML 文件 +tree = ET.parse('annotations.xml') +root = tree.getroot() + +# 清空所有 polygon 的 points 属性 +for polygon in root.iter('polygon'): + polygon.set('points', '') + +# 保存回文件(或另存为新文件) +tree.write('annotations_cleared.xml', encoding='utf-8', xml_declaration=True) \ No newline at end of file diff --git a/推理图片反向上传CVAT/seg/yolo_seg_infer_vis—f.py b/推理图片反向上传CVAT/seg/yolo_seg_infer_vis—f.py new file mode 100644 index 0000000..8516978 --- /dev/null +++ b/推理图片反向上传CVAT/seg/yolo_seg_infer_vis—f.py @@ -0,0 +1,125 @@ +import cv2 +import numpy as np +import os +from ultralytics import YOLO + +# ---------------- 配置 ---------------- +MODEL_PATH = "/home/hx/yolo/ultralytics_yolo11-main/runs/train/61seg/exp3/weights/best.pt" +IMAGE_DIR = "/media/hx/04e879fa-d697-4b02-ac7e-a4148876ebb0/dataset/6111c/1" +OUT_DIR = "./labels" # 只保存 labels +IMG_SIZE = 640 +CONF_THRES = 0.25 + +# 多边形简化比例(点数控制核心参数) +EPSILON_RATIO = 0.01 + +IMG_EXTS = (".jpg", ".jpeg", ".png", ".bmp") +# ------------------------------------- + + +def simplify_polygon(poly, epsilon_ratio): + """ + 使用 approxPolyDP 简化多边形 + """ + poly = poly.astype(np.int32) + perimeter = cv2.arcLength(poly, True) + epsilon = epsilon_ratio * perimeter + approx = cv2.approxPolyDP(poly, epsilon, True) + return approx.reshape(-1, 2) + + +def extract_simplified_masks(result): + """ + 提取并简化 YOLO mask + 返回: + [(cls_id, poly), ...] + """ + simplified = [] + + if result.masks is None: + return simplified + + boxes = result.boxes + + for i, poly in enumerate(result.masks.xy): + cls_id = int(boxes.cls[i]) + conf = float(boxes.conf[i]) + + if conf < CONF_THRES: + continue + + poly = simplify_polygon(poly, EPSILON_RATIO) + + if len(poly) < 3: + continue + + simplified.append((cls_id, poly)) + + return simplified + + +def save_yolo_seg_labels(masks, img_shape, save_path): + """ + 保存 YOLO segmentation 标签 + """ + h, w = img_shape[:2] + lines = [] + + for cls_id, poly in masks: + poly_norm = [] + for x, y in poly: + poly_norm.append(f"{x / w:.6f}") + poly_norm.append(f"{y / h:.6f}") + + lines.append(str(cls_id) + " " + " ".join(poly_norm)) + + # ⚠️ 没有目标也生成空 txt(YOLO 训练需要) + with open(save_path, "w") as f: + if lines: + f.write("\n".join(lines)) + + +def run_folder_inference(): + out_lbl_dir = os.path.join(OUT_DIR, "labels") + os.makedirs(out_lbl_dir, exist_ok=True) + + # 模型只加载一次 + model = YOLO(MODEL_PATH) + + img_files = sorted([ + f for f in os.listdir(IMAGE_DIR) + if f.lower().endswith(IMG_EXTS) + ]) + + print(f"📂 共检测 {len(img_files)} 张图片") + + for idx, img_name in enumerate(img_files, 1): + img_path = os.path.join(IMAGE_DIR, img_name) + img = cv2.imread(img_path) + + if img is None: + print(f"⚠️ 跳过无法读取: {img_name}") + continue + + results = model( + img, + imgsz=IMG_SIZE, + conf=CONF_THRES, + verbose=False + ) + result = results[0] + + masks = extract_simplified_masks(result) + + base_name = os.path.splitext(img_name)[0] + label_path = os.path.join(out_lbl_dir, base_name + ".txt") + + save_yolo_seg_labels(masks, img.shape, label_path) + + print(f"[{idx}/{len(img_files)}] ✅ {img_name}") + + print("🎉 标签生成完成(仅保存 YOLO Seg 标签)") + + +if __name__ == "__main__": + run_folder_inference()