Bläddra i källkod

add huyan to pokou

zhaoyinghan 1 månad sedan
förälder
incheckning
1a535643d1
70 ändrade filer med 5575 tillägg och 235 borttagningar
  1. 0 116
      models/line_detect/cs_readv.py
  2. 39 6
      models/line_detect/heads/head_losses.py
  3. 1 1
      models/line_detect/heads/ins/ins_predictor.py
  4. 116 79
      models/line_detect/line_dataset.py
  5. 1 1
      models/line_detect/line_detect.py
  6. 81 26
      models/line_detect/loi_heads.py
  7. 2 2
      models/line_detect/train.yaml
  8. 5 1
      models/line_detect/train_demo.py
  9. 55 3
      models/line_detect/trainer.py
  10. 169 0
      utils/data_process/csv_circle/csv_read.py
  11. 81 0
      utils/data_process/csv_circle/csv_show.py
  12. 32 0
      utils/data_process/csv_circle/resice.py
  13. 102 0
      utils/data_process/d_data_spliter.py
  14. 51 0
      utils/data_process/filter_two+pts/a.py
  15. 70 0
      utils/data_process/machine/resetjson.py
  16. 69 0
      utils/data_process/show_feature.py
  17. 59 0
      utils/data_process/show_prams.py
  18. 91 0
      utils/data_process/showarc.py
  19. 58 0
      utils/data_process/zyh/Dilation/expansion.py
  20. 91 0
      utils/data_process/zyh/Dilation/val_expansion.py
  21. 181 0
      utils/data_process/zyh/circle_rgb/a_4point.py
  22. 81 0
      utils/data_process/zyh/circle_rgb/b_pcd2rgbd.py
  23. 35 0
      utils/data_process/zyh/circle_rgb/c_matchtiffjson.py
  24. 16 0
      utils/data_process/zyh/circle_rgb/d_rename.py
  25. 102 0
      utils/data_process/zyh/circle_rgb/e_data_spliter.py
  26. 73 0
      utils/data_process/zyh/circle_rgb/show.py
  27. 94 0
      utils/data_process/zyh/circle_rgb/via/ajson.py
  28. 63 0
      utils/data_process/zyh/circle_rgb/via/show.py
  29. 102 0
      utils/data_process/zyh/d_data_spliter.py
  30. 100 0
      utils/data_process/zyh/dataset_util.py
  31. 80 0
      utils/data_process/zyh/match_pcd_new_jpg.py
  32. 44 0
      utils/data_process/zyh/matchold_newjpg.py
  33. 85 0
      utils/data_process/zyh/pcd2clolor_init.py
  34. 78 0
      utils/data_process/zyh/pcd2color.py
  35. 84 0
      utils/data_process/zyh/pcd2tiff.py
  36. 119 0
      utils/data_process/zyh/predict/pridict.py
  37. 89 0
      utils/data_process/zyh/rgbd/1fastrgbd.py
  38. 29 0
      utils/data_process/zyh/rgbd/2rename.py
  39. 156 0
      utils/data_process/zyh/rgbd/circle/a_circle.py
  40. 81 0
      utils/data_process/zyh/rgbd/circle/b_pcd2rgbd.py
  41. 35 0
      utils/data_process/zyh/rgbd/circle/c_matchtiffjson.py
  42. 16 0
      utils/data_process/zyh/rgbd/circle/d_rename.py
  43. 102 0
      utils/data_process/zyh/rgbd/circle/e_data_spliter.py
  44. 58 0
      utils/data_process/zyh/rgbd/circle/show.py
  45. 139 0
      utils/data_process/zyh/rgbd/mask_choumi/mask.py
  46. 55 0
      utils/data_process/zyh/rgbd/mask_choumi/show.py
  47. 156 0
      utils/data_process/zyh/rgbd/mask_xishu/manycircle.py
  48. 127 0
      utils/data_process/zyh/rgbd/mask_xishu/mask.py
  49. 58 0
      utils/data_process/zyh/rgbd/mask_xishu/show.py
  50. 81 0
      utils/data_process/zyh/rgbd/pcd2rgbd.py
  51. 15 0
      utils/data_process/zyh/rgbd/test/1delstrongjson.py
  52. 96 0
      utils/data_process/zyh/rgbd/test/3weizhuang.py
  53. 156 0
      utils/data_process/zyh/rgbd/test/4manycircle.py
  54. 58 0
      utils/data_process/zyh/rgbd/test/5show.py
  55. 35 0
      utils/data_process/zyh/rgbd/test/6matchtiffjson.py
  56. 16 0
      utils/data_process/zyh/rgbd/test/7rename.py
  57. 102 0
      utils/data_process/zyh/rgbd/test/8data_spliter.py
  58. 28 0
      utils/data_process/zyh/rgbd/tiffshow.py
  59. BIN
      utils/data_process/zyh/simplify_process_and_enhance_data/._.DS_Store
  60. 0 0
      utils/data_process/zyh/simplify_process_and_enhance_data/__init__.py
  61. 321 0
      utils/data_process/zyh/simplify_process_and_enhance_data/a_data_augmentation.py
  62. 254 0
      utils/data_process/zyh/simplify_process_and_enhance_data/b_process_json.py
  63. 47 0
      utils/data_process/zyh/simplify_process_and_enhance_data/c_pic_to_512.py
  64. 102 0
      utils/data_process/zyh/simplify_process_and_enhance_data/d_data_spliter.py
  65. 88 0
      utils/data_process/zyh/source_color.py
  66. 167 0
      utils/data_process/zyh/tiff_change_pic_reshape.py
  67. 70 0
      utils/data_process/zyh/toeveryone.py
  68. 112 0
      utils/data_process/zyh/tuoyuan_rgb/a_longshort.py
  69. 102 0
      utils/data_process/zyh/tuoyuan_rgb/d_data_spliter.py
  70. 114 0
      utils/data_process/zyh/xany2yolo/wyo.py

+ 0 - 116
models/line_detect/cs_readv.py

@@ -1,116 +0,0 @@
-import os
-import csv
-import json
-import math
-import ast
-import shutil
-
-def ellipse_to_box_points(cx, cy, rx, ry, theta):
-    """
-    ½«ÍÖÔ²²ÎÊýת»»Îª¾ØÐΣ¨ÉÏÏÂ×óÓÒ4µã£©
-    ½üËÆ·½·¨£ºÓÃÐýת¾ØÐεÄ4¸ö¶¥µã±íʾÍÖÔ²±ß½ç
-    """
-    # Ëĸöµã£¨×óÓÒÖе㣬ÉÏÏÂÖе㣩
-    points = [
-        [cx - rx, cy],  # ×ó
-        [cx + rx, cy],  # ÓÒ
-        [cx, cy - ry],  # ÉÏ
-        [cx, cy + ry],  # ÏÂ
-    ]
-
-    # ÐýתÕâЩµã
-    cos_t = math.cos(theta)
-    sin_t = math.sin(theta)
-    rotated_points = []
-    for x, y in points:
-        x_new = (x - cx) * cos_t - (y - cy) * sin_t + cx
-        y_new = (x - cx) * sin_t + (y - cy) * cos_t + cy
-        rotated_points.append([x_new, y_new])
-
-    return rotated_points
-
-
-def csv_to_labelme_json(csv_path, img_folder, output_folder, image_exts=(".png", ".tiff")):
-    os.makedirs(output_folder, exist_ok=True)
-
-    with open(csv_path, newline='', encoding='utf-8') as csvfile:
-        reader = csv.DictReader(csvfile)
-        for row in reader:
-            filename = row["filename"]
-            region_shape_attributes = row["region_shape_attributes"]
-
-            # ת³É×ֵ䣨CSV ÀïÊÇ×Ö·û´®£©
-            try:
-                attr = json.loads(region_shape_attributes)
-            except json.JSONDecodeError:
-                attr = ast.literal_eval(region_shape_attributes)
-
-            if attr.get("name") != "ellipse":
-                continue
-
-            cx, cy = float(attr["cx"]), float(attr["cy"])
-            rx, ry = float(attr["rx"]), float(attr["ry"])
-            theta = float(attr["theta"])
-
-            # ÍÖÔ²Ëĸöµã
-            points = ellipse_to_box_points(cx, cy, rx, ry, theta)
-
-            # »ñÈ¡xmin, ymin, xmax, ymax
-            xs = [p[0] for p in points]
-            ys = [p[1] for p in points]
-            xmin, ymin, xmax, ymax = int(min(xs)), int(min(ys)), int(max(xs)), int(max(ys))
-
-            # Éú³É Labelme ¸ñʽ
-            data = {
-                "version": "5.0.1",
-                "flags": {},
-                "shapes": [
-                    {
-                        "label": "circle",
-                        "points": points,
-                        "shape_type": "polygon",
-                        "flags": {},
-                        "xmin": xmin,
-                        "ymin": ymin,
-                        "xmax": xmax,
-                        "ymax": ymax,
-                    }
-                ],
-                "imagePath": filename.replace(".png", ".jpg").replace(".tiff", ".jpg"),
-                "imageHeight": 2000,  # Èç¹ûÄãÖªµÀʵ¼ÊͼÏñ³ß´ç¿ÉÒÔ¸Ä
-                "imageWidth": 2000
-            }
-
-            # дÈë JSON Îļþ
-            json_filename = os.path.splitext(filename)[0] + ".json"
-            json_path = os.path.join(output_folder, json_filename)
-            with open(json_path, "w", encoding="utf-8") as f:
-                json.dump(data, f, indent=4, ensure_ascii=False)
-
-            # ¿½±´Í¼Æ¬Îļþ
-            src_img_path = None
-            for ext in image_exts:
-                test_path = os.path.join(img_folder, filename.replace(".png", ext).replace(".tiff", ext))
-                if os.path.exists(test_path):
-                    src_img_path = test_path
-                    break
-
-            if src_img_path:
-                dst_img_path = os.path.join(output_folder, os.path.basename(src_img_path))
-                shutil.copy(src_img_path, dst_img_path)
-            else:
-                print(f"?? ÕÒ²»µ½Í¼Æ¬: {filename}")
-
-    print(f"? ת»»Íê³É£¬Êä³ö·¾¶£º{output_folder}")
-
-
-if __name__ == "__main__":
-    # ÊäÈëÎļþ¼Ð
-    csv_folder = "/data/share/zyh/数据集汇总/circle/huayan_circle/csv"
-    img_folder = "/data/share/zyh/数据集汇总/circle/huayan_circle/彩色图"
-    output_folder = "/home/zhaoyinghan/py_ws/data/circle/huayan"
-
-    for csv_file in os.listdir(csv_folder):
-        if csv_file.endswith(".csv"):
-            csv_path = os.path.join(csv_folder, csv_file)
-            csv_to_labelme_json(csv_path, img_folder, output_folder)

+ 39 - 6
models/line_detect/heads/head_losses.py

@@ -144,6 +144,29 @@ def single_point_to_heatmap(keypoints, rois, heatmap_size):
 
     return all_roi_heatmap
 
+def arc_inference1(arc_equation, x, arc_boxes, th):
+    # type: (Tensor, List[Tensor]) -> Tuple[List[Tensor], List[Tensor]]
+
+    points_probs = []
+    points_scores = []
+
+    print(f'arc_boxes:{len(arc_boxes)}')
+
+    boxes_per_image = [box.size(0) for box in arc_boxes]
+    print(f'arc boxes_per_image:{boxes_per_image}')
+    x2 = x.split(boxes_per_image, dim=0)
+    arc7 = arc_equation.split(boxes_per_image, dim=0)
+    # print(f'arc7:{arc7}')
+
+    for xx, bb in zip(x2, arc_boxes):
+        point_prob, point_scores = heatmaps_to_arc(xx, bb)
+
+        points_probs.append(point_prob.unsqueeze(1))
+        points_scores.append(point_scores)
+
+    return arc7, points_scores
+
+
 def points_to_heatmap(keypoints, rois,num_points=2, heatmap_size=(512,512)):
     # type: (Tensor, Tensor, int) -> Tensor
     print(f'rois:{rois.shape}')
@@ -1253,18 +1276,28 @@ def heatmaps_to_arc(maps, rois, threshold=0.5, output_size=(128, 128)):
         w = int((x2 - x1).item())
         # È·±£ bin_mask ÊÇ [1, 128, 128]
         assert bin_mask.dim() == 4, f"Expected 3D tensor [1, H, W], got {bin_mask.shape}"
-
-        # ÉϲÉÑù»Ø ROI ԭʼ´óС
         bin_mask_original_size = F.interpolate(
-            # bin_mask.unsqueeze(0),  # ? [1, 1, 128, 128]
-            bin_mask,  # ? [1, 1, 128, 128]
+            bin_mask,  # [1, 1, 128, 128]
             size=(h, w),
             mode='bilinear',
             align_corners=False
-        )[0]  # ? [1, h, w]
+        )[0]  # [1, h, w]
+
+        bin_mask_original_size = bin_mask_original_size[0]  # [h, w]
 
-        masks[i, 0, y1:y2, x1:x2] = bin_mask_original_size.squeeze()
+        masks[i, 0, y1:y2, x1:x2] = bin_mask_original_size
         scores[i] = bin_mask_original_size.sum()
+        #
+        # bin_mask_original_size = F.interpolate(
+        #     # bin_mask.unsqueeze(0),  # ? [1, 1, 128, 128]
+        #     bin_mask,  # ? [1, 1, 128, 128]
+        #     size=(h, w),
+        #     mode='bilinear',
+        #     align_corners=False
+        # )[0]  # ? [1, h, w]
+        #
+        # masks[i, 0, y1:y2, x1:x2] = bin_mask_original_size.squeeze()
+        # scores[i] = bin_mask_original_size.sum()
 
 
         # plt.figure(figsize=(6, 6))

+ 1 - 1
models/line_detect/heads/ins/ins_predictor.py

@@ -7,7 +7,7 @@ import torch.nn.functional as F
 
 
 class ArcEquationPredictor(nn.Module):
-    def __init__(self, h=512, w=672, num_outputs=7):
+    def __init__(self, h=672, w=672, num_outputs=7):
         super().__init__()
         self.h = h
         self.w = w

+ 116 - 79
models/line_detect/line_dataset.py

@@ -17,6 +17,7 @@ import torch
 
 import matplotlib.pyplot as plt
 from models.base.transforms import get_transforms
+from utils.data_process.show_prams import print_params
 
 
 def validate_keypoints(keypoints, image_width, image_height):
@@ -93,44 +94,52 @@ class LineDataset(BaseDataset):
         target = {}
 
         target["image_id"] = torch.tensor(item)
-        boxes, lines, points, arc_mask, circle_4points, labels, arc_ends, arc_params = get_boxes_lines(objs, shape)
+
+        #boxes, line_point_pairs, points, labels, mask_ends, mask_params
+        boxes, lines, points, labels, arc_ends, arc_params = get_boxes_lines(objs, shape)
+
+        # print_params(arc_ends, arc_params)
 
         if points is not None:
             target["points"] = points
+        # if lines is not None:
+        #     a = torch.full((lines.shape[0],), 2).unsqueeze(1)
+        #     lines = torch.cat((lines, a), dim=1)
+        #     target["lines"] = lines.to(torch.float32).view(-1, 2, 3)
         if lines is not None:
-            a = torch.full((lines.shape[0],), 2).unsqueeze(1)
-            lines = torch.cat((lines, a), dim=1)
-            target["lines"] = lines.to(torch.float32).view(-1, 2, 3)
-            # print(f'lines shape:{ target["lines"].shape}')
-
-        if arc_mask is not None:
-            target['arc_mask'] = arc_mask
-            # print(f'arc_mask dataset')
-        # else:
-        #     print(f'not arc_mask dataset')
+            label_3d = labels.view(-1, 1, 1).expand(-1, 2, -1)  # [N] -> [N,2,1]
+            line1 = torch.cat([lines, label_3d], dim=-1)  # [N,2,3]
+            target["lines"] = line1.to(torch.float32)
 
         if arc_ends is not None:
             target['mask_ends'] = arc_ends
+        if arc_params is not None:
             target['mask_params'] = arc_params
 
-            arc_angles = compute_arc_angles(arc_ends, arc_params)
-            # print(arc_angles)
-            # print(arc_params)
 
+            arc_angles = compute_arc_angles(arc_ends, arc_params)
+            # print_params(arc_angles)
             arc_masks = []
+
+
+
             for i in range(len(arc_params)):
-                arc7=arc_params[i] + arc_angles[i].tolist()
-                arc_masks.append(arc_to_mask(arc7, shape, line_width=1))
+                arc_param_i = arc_params[i].view(-1)  # shape (5,)
+                arc_angle_i = arc_angles[i].view(-1)  # shape (2,)
+                arc7 = torch.cat([arc_param_i, arc_angle_i], dim=0)  # shape (7,)
+
 
-            print(f'arc_masks:{torch.stack(arc_masks, dim=0).shape}')
-            target['arc_masks'] = torch.stack(arc_masks, dim=0)
+                print_params(arc7)
+                mask = arc_to_mask(arc7, shape, line_width=1)
 
+                arc_masks.append(mask)
+                # arc7=arc_params[i] + arc_angles[i].tolist()
+                # arc_masks.append(arc_to_mask(arc7, shape, line_width=1))
+
+            # print(f'circle_masks:{torch.stack(arc_masks, dim=0).shape}')
+            target['circle_masks'] = torch.stack(arc_masks, dim=0)
 
 
-        if circle_4points is not None:
-            target['circles'] = circle_4points
-            circle_masks = generate_ellipse_mask(shape, points_to_ellipse(circle_4points))
-            target['circle_masks'] = torch.tensor(circle_masks, dtype=torch.float32).unsqueeze(0)
 
         target["boxes"] = boxes
         target["labels"] = labels
@@ -256,7 +265,11 @@ def arc_to_mask(arc7, shape, line_width=1):
     Returns:
         mask (Tensor): [H, W], dtype=torch.uint8, 0/255
     """
+    # print_params(arc7)
     # 确保 phi1 -> phi2 是正向(可处理跨 2π 的情况)
+    if torch.all(torch.tensor(arc7) == 0):
+        return torch.zeros(shape, dtype=torch.uint8)
+
     xc, yc, a, b, theta, phi1, phi2 = arc7
     H, W = shape
     if phi2 < phi1:
@@ -285,7 +298,7 @@ def arc_to_mask(arc7, shape, line_width=1):
     # 绘制折线(antialias=False 更适合 mask)
     cv2.polylines(img, [points], isClosed=False, color=255, thickness=line_width, lineType=cv2.LINE_AA)
 
-    return torch.from_numpy(img).byte()  # [H, W], values: 0 or 255
+    return torch.from_numpy(img).float()  # [H, W], values: 0 or 255
 
 
 def compute_arc_angles(gt_mask_ends, gt_mask_params):
@@ -299,10 +312,13 @@ def compute_arc_angles(gt_mask_ends, gt_mask_params):
     Returns:
         phi: float, in [0, 2*pi)
     """
+    # print_params(gt_mask_ends, gt_mask_params)
     results = []
-    gt_mask_params_tensor = torch.tensor(gt_mask_params,
-                                         dtype=gt_mask_ends.dtype,
-                                         device=gt_mask_ends.device)
+    if not isinstance(gt_mask_params, torch.Tensor):
+        gt_mask_params_tensor = torch.tensor(gt_mask_params, dtype=gt_mask_ends.dtype, device=gt_mask_ends.device)
+    else:
+        gt_mask_params_tensor = gt_mask_params.clone().detach().to(gt_mask_ends)
+
     for ends_img, params_img in zip(gt_mask_ends, gt_mask_params_tensor):
         # print(f'params_img:{params_img}')
         if torch.norm(params_img) < 1e-6:  # L2 norm near zero
@@ -411,12 +427,11 @@ def get_boxes_lines(objs, shape):
     boxes = []
     labels = []
     h, w = shape
+
     line_point_pairs = []
     points = []
-    arc_mask = []
-    arc_ends = []
-    arc_params = []
-    circle_4points = []
+    mask_ends = []
+    mask_params = []
 
     for obj in objs:
         # plt.plot([a[1], b[1]], [a[0], b[0]], c="red", linewidth=1)  # a[1], b[1]无明确大小
@@ -426,8 +441,9 @@ def get_boxes_lines(objs, shape):
         if label == 'line' or label == 'dseam1':
             a, b = obj['points'][0], obj['points'][1]
 
-            line_point_pairs.append(a)
-            line_point_pairs.append(b)
+            # line_point_pairs.append(a)
+            # line_point_pairs.append(b)
+            line_point_pairs.append([a, b])
 
             xmin = max(0, (min(a[0], b[0]) - 6))
             xmax = min(w, (max(a[0], b[0]) + 6))
@@ -437,6 +453,13 @@ def get_boxes_lines(objs, shape):
             boxes.append([xmin, ymin, xmax, ymax])
             labels.append(torch.tensor(2))
 
+            points.append(torch.tensor([0.0]))
+            mask_ends.append([[0, 0], [0, 0]])
+            mask_params.append([0, 0, 0, 0, 0])
+            # circle_4points.append([[0, 0], [0, 0], [0, 0], [0, 0]])
+
+
+
         elif label == 'point':
             p = obj['points'][0]
             xmin = max(0, p[0] - 12)
@@ -448,72 +471,86 @@ def get_boxes_lines(objs, shape):
             labels.append(torch.tensor(1))
             boxes.append([xmin, ymin, xmax, ymax])
 
+            line_point_pairs.append([[0, 0], [0, 0]])
+            mask_ends.append([[0, 0], [0, 0]])
+            mask_params.append([0, 0, 0, 0, 0])
+            # circle_4points.append([[0, 0], [0, 0], [0, 0], [0, 0]])
+
+
+        # elif label == 'arc':
+        #     arc_points = obj['points']
+        #     arc_params = obj['params']
+        #     arc_ends = obj['ends']
+        #     line_mask.append(arc_points)
+        #     mask_ends.append(arc_ends)
+        #     mask_params.append(arc_params)
+        #
+        #     xs = [p[0] for p in arc_points]
+        #     ys = [p[1] for p in arc_points]
+        #     xmin, xmax = min(xs), max(xs)
+        #     ymin, ymax = min(ys), max(ys)
+        #
+        #     boxes.append([xmin, ymin, xmax, ymax])
+        #     labels.append(torch.tensor(3))
+        #
+        #     points.append(torch.tensor([0.0]))
+        #     line_point_pairs.append([[0, 0], [0, 0]])
+        #     circle_4points.append([[0, 0], [0, 0], [0, 0], [0, 0]])
 
         elif label == 'arc':
-            arc_points = obj['points']
-            params = obj['params']
-            ends = obj['ends']
-            arc_ends.append(ends)
-            arc_params.append(params)
 
-            xs = [p[0] for p in arc_points]
-            ys = [p[1] for p in arc_points]
-            xmin, xmax = min(xs), max(xs)
-            ymin, ymax = min(ys), max(ys)
-
-            boxes.append([xmin, ymin, xmax, ymax])
-            labels.append(torch.tensor(3))
-
-        elif label == 'circle':
-            # print(f'len circle_4points: {len(obj['points'])}')
-            points = sort_points_clockwise(obj['points'])
-            circle_4points.append(points)
-
-            xmin = max(obj['xmin'] - 40, 0)
-            xmax = min(obj['xmax'] + 40, w)
-            ymin = max(obj['ymin'] - 40, 0)
-            ymax = min(obj['ymax'] + 40, h)
+            arc_params = obj['params']
+            arc_ends = obj['ends']
+            mask_ends.append(arc_ends)
+            mask_params.append(arc_params)
+            arc3points = obj['points']
+            xs = [p[0] for p in arc3points]
+            ys = [p[1] for p in arc3points]
+            xmin_raw = min(xs)
+            xmax_raw = max(xs)
+            ymin_raw = min(ys)
+            ymax_raw = max(ys)
+            xmin = max(xmin_raw - 40, 0)
+            xmax = min(xmax_raw + 40, w)
+            ymin = max(ymin_raw - 40, 0)
+            ymax = min(ymax_raw + 40, h)
 
             boxes.append([xmin, ymin, xmax, ymax])
             labels.append(torch.tensor(4))
+            points.append(torch.tensor([0.0]))
+            line_point_pairs.append([[0, 0], [0, 0]])
+
 
     boxes = torch.tensor(boxes, dtype=torch.float32)
     print(f'boxes:{boxes.shape}')
     labels = torch.tensor(labels)
-    if len(points) == 0:
-        points = None
-    else:
+
+    if points:
         points = torch.tensor(points, dtype=torch.float32)
-    print(f'read labels:{labels}')
-    # print(f'read points:{points}')
-    if len(line_point_pairs) == 0:
-        line_point_pairs = None
     else:
-        line_point_pairs = torch.tensor(line_point_pairs)
-        # print(f'line_point_pairs:{line_point_pairs.shape},{line_point_pairs.dtype}')
-
-    # print(f'boxes:{boxes.shape},line_point_pairs:{line_point_pairs.shape}')
+        points = None
 
-    if len(arc_mask) == 0:
-        arc_mask = None
+    if line_point_pairs:
+        line_point_pairs = torch.tensor(line_point_pairs, dtype=torch.float32)
     else:
-        arc_mask = torch.tensor(arc_mask, dtype=torch.float32)
-        print(f'arc_mask shape :{arc_mask.shape},{arc_mask.dtype}')
+        line_point_pairs = None
 
-    if len(arc_ends) == 0:
-        arc_ends = None
+    if mask_ends:
+        mask_ends = torch.tensor(mask_ends, dtype=torch.float32)
     else:
-        arc_ends = torch.tensor(arc_ends, dtype=torch.float32)
+        mask_ends = None
 
-    if len(circle_4points) == 0:
-        circle_4points = None
+    if mask_params:
+        mask_params = torch.tensor(mask_params, dtype=torch.float32)
     else:
-        # for circle_4point in circle_4points:
-        # print(f'circle_4point len111:{len(circle_4point)}')
-        circle_4points = torch.tensor(circle_4points, dtype=torch.float32)
-        # print(f'circle_4points shape:{circle_4points.shape}')
+        mask_params = None
+
+
+
+
+
 
-    return boxes, line_point_pairs, points, arc_mask, circle_4points, labels, arc_ends, arc_params
+    return boxes, line_point_pairs, points, labels, mask_ends, mask_params
 
 
 if __name__ == '__main__':

+ 1 - 1
models/line_detect/line_detect.py

@@ -518,7 +518,7 @@ def linedetect_efficientnet(
     # weights = LineNet_ResNet50_FPN_Weights.verify(weights)
     # weights_backbone = ResNet50_Weights.verify(weights_backbone)
     if num_classes is None:
-        num_classes = 5
+        num_classes = 7
     if num_points is None:
         num_points = 3
 

+ 81 - 26
models/line_detect/loi_heads.py

@@ -14,7 +14,8 @@ from collections import OrderedDict
 
 from models.line_detect.heads.head_losses import point_inference, compute_point_loss, line_iou_loss, \
     lines_point_pair_loss, features_align, line_inference, compute_ins_loss, ins_inference, compute_circle_loss, \
-    circle_inference
+    circle_inference, arc_inference1
+from utils.data_process.show_prams import print_params
 
 
 def fastrcnn_loss(class_logits, box_regression, labels, regression_targets):
@@ -1365,13 +1366,15 @@ class RoIHeads(nn.Module):
             # circle_proposals_tensor=torch.cat(circle_proposals)
 
             ins_proposals_valid = self.check_proposals(ins_proposals)
-
+            print(f"self.train{self.training}")
+            print(f"self.val{ins_proposals_valid}")
             if  ins_proposals_valid:
 
 
 
                 print(f'features from backbone:{features['0'].shape}')
                 feature_logits = self.ins_forward1(features, image_shapes, ins_proposals)
+
                 arc_equation = self.arc_equation_head(feature_logits)  # [proposal和,7]
 
                 loss_ins = None
@@ -1380,6 +1383,7 @@ class RoIHeads(nn.Module):
                 loss_arc_ends = None
 
                 if self.training:
+                    print("circle loss!!!!!!")
 
                     if targets is None or ins_pos_matched_idxs is None:
                         raise ValueError("both targets and pos_matched_idxs should not be None when in training mode")
@@ -1391,12 +1395,13 @@ class RoIHeads(nn.Module):
                     gt_mask_ends = [t["mask_ends"] for t in targets if "mask_ends" in t]
                     gt_mask_params = [t["mask_params"] for t in targets if "mask_params" in t]
 
-                    print(f'gt_ins:{gt_inses[0].shape}')
+                    # print(f'gt_ins:{gt_inses[0].shape}')
                     h, w = targets[0]["img_size"]
                     img_size = h
 
                     gt_ins_tensor = torch.zeros(0, 0)
                     if len(gt_inses) > 0:
+                        print_params(gt_inses)
                         gt_ins_tensor = torch.cat(gt_inses)
                         print(f'gt_ins_tensor:{gt_ins_tensor.shape}')
 
@@ -1406,7 +1411,6 @@ class RoIHeads(nn.Module):
                         loss_ins = compute_ins_loss(feature_logits, ins_proposals, gt_inses,ins_pos_matched_idxs)
                         total_loss, loss_arc_equation, loss_arc_ends = compute_arc_equation_loss(arc_equation,
                                                                                                  ins_proposals,
-
                                                                                                  gt_mask_ends,
                                                                                                  gt_mask_params,
                                                                                                  ins_pos_matched_idxs,
@@ -1428,8 +1432,11 @@ class RoIHeads(nn.Module):
                         print(f'loss_ins_extra is None111')
                         loss_ins_extra = torch.tensor(0.0, device=device)
 
-                    loss_ins = {"loss_ins": loss_ins,"loss_arc_equation": loss_arc_equation,"loss_arc_ends": loss_arc_ends}
+                    loss_ins = {"loss_ins": loss_ins}
                     loss_ins_extra = {"loss_ins_extra": loss_ins_extra}
+                    loss_arc_equation = {"loss_arc_equation": loss_arc_equation}
+                    loss_arc_ends = {"loss_arc_ends": loss_arc_ends}
+
 
                 else:
                     if targets is not None:
@@ -1437,6 +1444,8 @@ class RoIHeads(nn.Module):
                         img_size = h
                         gt_inses = [t["circle_masks"] for t in targets if "circle_masks" in t]
                         gt_labels = [t["labels"] for t in targets]
+                        gt_mask_ends = [t["mask_ends"] for t in targets if "mask_ends" in t]
+                        gt_mask_params = [t["mask_params"] for t in targets if "mask_params" in t]
                         gt_ins_tensor = torch.zeros(0, 0)
                         if len(gt_inses) > 0:
                             gt_ins_tensor = torch.cat(gt_inses)
@@ -1447,6 +1456,9 @@ class RoIHeads(nn.Module):
 
                             loss_ins = compute_ins_loss(feature_logits, ins_proposals, gt_inses,
                                                            ins_pos_matched_idxs)
+                            total_loss, loss_arc_equation, loss_arc_ends = compute_arc_equation_loss(
+                                arc_equation, ins_proposals, gt_mask_ends, gt_mask_params, ins_pos_matched_idxs, labels)
+                            loss_arc_ends = loss_arc_ends
 
                             # loss_ins_extra = compute_circle_extra_losses(feature_logits, circle_proposals, gt_circles,circle_pos_matched_idxs)
 
@@ -1458,14 +1470,34 @@ class RoIHeads(nn.Module):
                             print(f'loss_ins_extra is None111')
                             loss_ins_extra = torch.tensor(0.0, device=device)
 
+                        if loss_arc_equation is None:
+                            print(f'loss_arc_equation is None')
+                            loss_arc_equation = torch.tensor(0.0, device=device)
+
+                        if loss_arc_ends is None:
+                            print(f'loss_arc_ends is None')
+                            loss_arc_ends = torch.tensor(0.0, device=device)
+
+                        if loss_ins is None:
+                            print(f'loss_ins is None111')
+                            loss_ins = torch.tensor(0.0, device=device)
+
+                        if loss_ins_extra is None:
+                            print(f'loss_ins_extra is None111')
+                            loss_ins_extra = torch.tensor(0.0, device=device)
+
                         loss_ins = {"loss_ins": loss_ins}
                         loss_ins_extra = {"loss_ins_extra": loss_ins_extra}
+                        loss_arc_equation = {"loss_arc_equation": loss_arc_equation}
+                        loss_arc_ends = {"loss_arc_ends": loss_arc_ends}
 
 
 
                     else:
                         loss_ins = {}
                         loss_ins_extra = {}
+                        loss_arc_equation = {}
+                        loss_arc_ends = {}
                         if feature_logits is None or ins_proposals is None:
                             raise ValueError(
                                 "both keypoint_logits and keypoint_proposals should not be None when not in training mode"
@@ -1475,6 +1507,11 @@ class RoIHeads(nn.Module):
 
                             ins_masks, ins_scores, circle_points = ins_inference(feature_logits,
                                                                                          ins_proposals, th=0)
+
+                            arc7, arc_scores = arc_inference1(arc_equation, feature_logits, ins_proposals, 0.5)
+                            for arc_, arc_score, r in zip(arc7, arc_scores, result):
+                                r["arcs"] = arc_
+                                r["arc_scores"] = arc_score
                             # print(f'circles_probs:{circles_probs.shape}, circles_scores:{circles_scores.shape}')
                             proposals_per_image = [box.size(0) for box in ins_proposals]
                             print(f'ins_proposals_per_image:{proposals_per_image}')
@@ -1498,6 +1535,8 @@ class RoIHeads(nn.Module):
                 print(f'loss_ins_extra:{loss_ins_extra}')
                 losses.update(loss_ins)
                 losses.update(loss_ins_extra)
+                losses.update(loss_arc_equation)
+                losses.update(loss_arc_ends)
                 print(f'losses:{losses}')
 
 
@@ -1791,42 +1830,58 @@ def compute_arc_equation_loss(arc_equation, proposals, gt_mask_ends, gt_mask_par
             gt_sel_angles.append(po)
             gt_sel_params.append(pa)
 
-    print(f'gt_sel_angles:{gt_sel_angles}')
-    print(f'gt_sel_params:{gt_sel_params}')
+
 
     gt_sel_angles = torch.cat(gt_sel_angles, dim=0)
     gt_sel_params = torch.cat(gt_sel_params, dim=0)
-
-    print(f'gt_sel_angles:{gt_sel_angles}')
-    print(f'gt_sel_params:{gt_sel_params}')
-
     pred_angles = arc_equation[:, 5:7]
     pred_params = arc_equation[:, :5]
 
-    angle_loss = F.mse_loss(pred_angles, gt_sel_angles)
-    param_loss = F.mse_loss(pred_params.cpu(), gt_sel_params) / 10000
-    print(f'angle_loss:{angle_loss}, param_loss:{param_loss}')
+    print_params(pred_angles, pred_params, gt_sel_angles, gt_sel_params)
 
-    count = sum(len(sublist) for sublist in proposals)
+    pred_sin = torch.sin(pred_angles)
+    pred_cos = torch.cos(pred_angles)
+    gt_sin = torch.sin(gt_sel_angles)
+    gt_cos = torch.cos(gt_sel_angles)
+    angle_loss = F.mse_loss(pred_sin, gt_sin) + F.mse_loss(pred_cos, gt_cos)
 
-    total_loss = (param_loss + angle_loss) / count if count > 0 else torch.tensor(0.0)
 
-    # 确保 dtype 和 device
-    total_loss = total_loss.float().to(device)
-    angle_loss = angle_loss.float().to(device)
-    param_loss = param_loss.float().to(device)
+    param_loss = F.mse_loss(pred_params, gt_sel_params) / 10000
 
-    # if count > 0:
-    #     total_loss = (param_loss + angle_loss) / count
-    #     total_loss = torch.tensor(total_loss, dtype=torch.float32, device=device)
-    # else:
-    #     total_loss = torch.tensor(0.0, dtype=torch.float32, device=device)
+    print(f'angle_loss:{angle_loss.item()}, param_loss:{param_loss.item()}')
 
-    print(f'total_loss, param_loss, angle_loss:{total_loss, param_loss, angle_loss}')
+
+    count = sum(len(sublist) for sublist in proposals)
+    total_loss = ((param_loss + angle_loss) / count) if count > 0 else torch.tensor(0.0, device=device,
+                                                                                    dtype=torch.float)
+
+    total_loss = total_loss.to(device)
+    angle_loss = angle_loss.to(device)
+    param_loss = param_loss.to(device)
+
+    print(f'total_loss, param_loss, angle_loss: {total_loss.item()}, {param_loss.item()}, {angle_loss.item()}')
 
     return total_loss, param_loss, angle_loss
 
 
+    # angle_loss = F.mse_loss(pred_angles, gt_sel_angles)
+    # param_loss = F.mse_loss(pred_params.cpu(), gt_sel_params) / 10000
+    # print(f'angle_loss:{angle_loss}, param_loss:{param_loss}')
+    #
+    # count = sum(len(sublist) for sublist in proposals)
+    #
+    # total_loss = (param_loss + angle_loss) / count if count > 0 else torch.tensor(0.0)
+    #
+    # # 确保 dtype 和 device
+    # total_loss = total_loss.float().to(device)
+    # angle_loss = angle_loss.float().to(device)
+    # param_loss = param_loss.float().to(device)
+    #
+    # print(f'total_loss, param_loss, angle_loss:{total_loss, param_loss, angle_loss}')
+    #
+    # return total_loss, param_loss, angle_loss
+
+
 def compute_arc_angles(gt_mask_ends, gt_mask_params):
     """
     给定椭圆上的一个点,计算其对应的参数角 phi(弧度)。

+ 2 - 2
models/line_detect/train.yaml

@@ -7,7 +7,7 @@ io:
 #  datadir: /data/share/rlq/datasets/250718caisegangban
 
 #  datadir: /data/share/rlq/datasets/singepoint_Dataset0709_2
-  datadir: /data/share/zyh/master_dataset/circle/huyan_eclipse/a_dataset
+  datadir: /data/share/zyh/master_dataset/pokou/251115/a_dataset_pokou_mask
 #  datadir: \\192.168.50.222/share/rlq/datasets/singepoint_Dataset0709_2
 #  datadir: \\192.168.50.222/share/rlq/datasets/250718caisegangban
   data_type: rgb
@@ -20,7 +20,7 @@ io:
 train_params:
   resume_from:
   num_workers: 8
-  batch_size: 2
+  batch_size: 1
   max_epoch: 8000000
 #  augmentation: True
   augmentation: False

+ 5 - 1
models/line_detect/train_demo.py

@@ -1,4 +1,5 @@
 import torch
+import os
 
 from models.line_detect.line_detect import linedetect_newresnet18fpn, linedetect_resnet50_fpn, linedetect_resnet18_fpn, \
     linedetect_newresnet50fpn, linedetect_maxvitfpn, linedetect_high_maxvitfpn, linedetect_swin_transformer_fpn, \
@@ -9,6 +10,8 @@ from models.line_net.trainer import Trainer
 device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
 if __name__ == '__main__':
 
+    os.environ["CUDA_LAUNCH_BLOCKING"] = "1"
+
     # model = LineNet('line_net.yaml')
     # model=linedetect_resnet50_fpn()
     # model = linedetect_resnet50_fpn()
@@ -28,4 +31,5 @@ if __name__ == '__main__':
     # model=linedetect_high_maxvitfpn()
 
     # model=linedetect_swin_transformer_fpn(type='t')
-    model.start_train(cfg='train.yaml')
+    model.start_train(cfg='train.yaml')
+

+ 55 - 3
models/line_detect/trainer.py

@@ -5,6 +5,7 @@ from datetime import datetime
 import cv2
 import numpy as np
 import torch
+from PIL.ImageDraw import ImageDraw
 from matplotlib import pyplot as plt
 from scipy.ndimage import gaussian_filter
 from torch.optim.lr_scheduler import ReduceLROnPlateau
@@ -150,7 +151,51 @@ def fit_circle(points):
     r = np.sqrt(cx ** 2 + cy ** 2 - F)
 
     return (cx, cy), r
-
+from PIL import ImageDraw, Image
+import io
+# 绘制椭圆
+def draw_el(all, background_img):
+    # 解析椭圆参数
+    if isinstance(all, torch.Tensor):
+        all = all.cpu().numpy()
+    x, y, a, b, q, q1, q2 = all
+    theta = np.radians(q)
+    phi1 = np.radians(q1)  # 第一个点的参数角
+    phi2 = np.radians(q2)  # 第二个点的参数角
+
+    # 生成椭圆上的点
+    phi = np.linspace(0, 2 * np.pi, 500)
+    x_ellipse = x + a * np.cos(phi) * np.cos(theta) - b * np.sin(phi) * np.sin(theta)
+    y_ellipse = y + a * np.cos(phi) * np.sin(theta) + b * np.sin(phi) * np.cos(theta)
+
+    # 计算两个指定点的坐标
+    def param_to_point(phi, xc, yc, a, b, theta):
+        x = xc + a * np.cos(phi) * np.cos(theta) - b * np.sin(phi) * np.sin(theta)
+        y = yc + a * np.cos(phi) * np.sin(theta) + b * np.sin(phi) * np.cos(theta)
+        return x, y
+
+    P1 = param_to_point(phi1, x, y, a, b, theta)
+    P2 = param_to_point(phi2, x, y, a, b, theta)
+
+    # 创建画布并显示背景图片(使用传入的background_img,shape为[H, W, C])
+    plt.figure(figsize=(10, 10))
+    plt.imshow(background_img)  # 直接显示背景图
+
+    # 绘制椭圆及相关元素
+    plt.plot(x_ellipse, y_ellipse, 'b-', linewidth=2)
+    plt.plot(x, y, 'ko', markersize=8)
+    plt.plot(P1[0], P1[1], 'ro', markersize=10)
+    plt.plot(P2[0], P2[1], 'go', markersize=10)
+
+    # 转换为TensorBoard所需的张量格式 [C, H, W]
+    buf = io.BytesIO()
+    plt.savefig(buf, format='png', bbox_inches='tight')
+    buf.seek(0)
+    result_img = Image.open(buf).convert('RGB')
+    img_tensor = torch.from_numpy(np.array(result_img)).permute(2, 0, 1)
+    plt.close()
+
+    return img_tensor
 # 由低到高蓝黄红
 def draw_lines_with_scores(tensor_image, lines, scores, width=3, cmap='viridis'):
     """
@@ -327,12 +372,16 @@ class Trainer(BaseTrainer):
 
         if 'arcs' in result:
             arcs = result['arcs'][0]
+            print(f'arcs in dra w:{arcs}')
 
-            # img_rgb = cv2.cvtColor(img_np, cv2.COLOR_BGR2RGB)
+            ellipse_img = draw_el(arcs, background_img=im)
 
+            # img_rgb = cv2.cvtColor(img_np, cv2.COLOR_BGR2RGB)
+            #
             # img_tensor =torch.tensor(img_rgb)
             # img_tensor = np.transpose(img_tensor)
-            self.writer.add_image('z-out-arc', arcs, global_step=epoch)
+
+            self.writer.add_image('z-out-arc', ellipse_img, global_step=epoch)
 
         if 'ins_masks' in result:
             # points=result['circles']
@@ -521,6 +570,8 @@ class Trainer(BaseTrainer):
                 # print(f'result:{result}')
                 t_end = time.time()
                 print(f'predict used:{t_end - t_start}')
+                from utils.data_process.show_prams import print_params
+                print_params(imgs[0], result[0], epoch)
                 self.writer_predict_result(img=imgs[0], result=result[0], epoch=epoch)
                 epoch_step+=1
 
@@ -529,6 +580,7 @@ class Trainer(BaseTrainer):
         self.writer.add_scalar(f'loss/{phase}', avg_loss, epoch)
         return model, avg_loss
 
+
     def save_best_model(self, model, save_path, epoch, current_loss, best_loss, optimizer=None):
         os.makedirs(os.path.dirname(save_path), exist_ok=True)
 

+ 169 - 0
utils/data_process/csv_circle/csv_read.py

@@ -0,0 +1,169 @@
+import os
+import csv
+import json
+import shutil
+import math
+from typing import List, Union, Dict
+
+# === 文件夹配置 ===
+csv_folder = r"\\192.168.50.222\share\zyh\master_dataset\pokou\remark_251104\params"  # CSV 文件夹
+json_folder = r"\\192.168.50.222\share\zyh\master_dataset\pokou\total"  # JSON 和图片文件夹
+output_folder = r"\\192.168.50.222\share\zyh\master_dataset\pokou\251115\csvjson"  # 输出文件夹
+
+os.makedirs(output_folder, exist_ok=True)
+
+def compute_arc_ends(points: List[List[float]]) -> List[List[float]]:
+    if len(points) != 3:
+        return [[0,0],[0,0]]
+
+    p1, p2, p3 = points
+    x1, y1 = p1
+    x2, y2 = p2
+    x3, y3 = p3
+
+    # --- 圆心 ---
+    A = 2*(x2-x1)
+    B = 2*(y2-y1)
+    C = x2**2+y2**2-x1**2-y1**2
+    D = 2*(x3-x2)
+    E = 2*(y3-y2)
+    F = x3**2+y3**2-x2**2-y2**2
+
+    denom = A*E - B*D
+    if denom == 0:
+        return [p1, p3]
+
+    cx = (C*E - F*B)/denom
+    cy = (A*F - D*C)/denom
+
+    angles = [math.atan2(y-cy, x-cx) for x,y in points]
+
+    def angle_diff(a1,a2):
+        diff = (a2-a1)%(2*math.pi)
+        if diff>math.pi: diff=2*math.pi-diff
+        return diff
+
+    pairs = [(0,1),(0,2),(1,2)]
+    max_diff=-1
+    end_pair=(0,1)
+    for i,j in pairs:
+        diff=angle_diff(angles[i],angles[j])
+        if diff>max_diff:
+            max_diff=diff
+            end_pair=(i,j)
+
+    return [points[end_pair[0]], points[end_pair[1]]]
+
+
+# === 工具函数:匹配点到最近椭圆 ===
+def match_point_to_ellipse(point: List[float], ellipses: List[Dict]) -> int:
+    """
+    根据点匹配到最近的椭圆
+    point: [x,y]
+    ellipses: list of dict,每个包含 cx,cy
+    return: ellipse_index
+    """
+    x, y = point
+    min_dist = float('inf')
+    match_idx = -1
+    for i, e in enumerate(ellipses):
+        cx, cy = e['cx'], e['cy']
+        dist = math.hypot(x - cx, y - cy)
+        if dist < min_dist:
+            min_dist = dist
+            match_idx = i
+    return match_idx
+
+# === 读取 CSV,构建文件名到椭圆参数映射 ===
+csv_ellipse_map = {}  # filename -> list of ellipse params
+for csv_file in os.listdir(csv_folder):
+    if not csv_file.endswith(".csv"):
+        continue
+    csv_path = os.path.join(csv_folder, csv_file)
+    with open(csv_path, "r", encoding="utf-8-sig") as f:
+        reader = csv.DictReader(f)
+        for row in reader:
+            filename = row["filename"].strip()
+            shape_str = row["region_shape_attributes"]
+            try:
+                shape_data = json.loads(shape_str)
+            except json.JSONDecodeError:
+                shape_data = json.loads(shape_str.replace('""', '"'))
+            if filename not in csv_ellipse_map:
+                csv_ellipse_map[filename] = []
+            csv_ellipse_map[filename].append(shape_data)
+
+# === 遍历 JSON 文件 ===
+for json_file in os.listdir(json_folder):
+    if not json_file.endswith(".json"):
+        continue
+    json_path = os.path.join(json_folder, json_file)
+    filename = json_file.replace(".json", ".jpg")
+    img_path = os.path.join(json_folder, filename)
+
+    if not os.path.exists(img_path):
+        print(f"?? Image not found for {filename}")
+        continue
+    if filename not in csv_ellipse_map:
+        print(f"?? CSV ellipse not found for {filename}")
+        continue
+
+    with open(json_path, "r", encoding="utf-8") as jf:
+        data = json.load(jf)
+
+    if "shapes" not in data:
+        data["shapes"] = []
+
+    # 收集所有 arc 单点
+    arc_points = [s["points"][0] for s in data["shapes"]
+                  if s.get("label")=="arc" and "points" in s and len(s["points"])==1]
+
+    # 根据 CSV 椭圆匹配分组
+    ellipses = csv_ellipse_map[filename]
+    ellipse_point_map = {i: [] for i in range(len(ellipses))}
+    for pt in arc_points:
+        idx = match_point_to_ellipse(pt, ellipses)
+        ellipse_point_map[idx].append(pt)
+
+    # 打印匹配到两个及以上椭圆的图片信息
+    active_ellipses = [i for i, pts in ellipse_point_map.items() if len(pts) >= 1]
+    if len(active_ellipses) >= 2:
+        print(f"Image {filename} matches {len(active_ellipses)} ellipses")
+
+    # 构造新的 arc_shape
+    new_arc_shapes = []
+    for idx, pts in ellipse_point_map.items():
+        if len(pts) != 3:
+            print(f"?? {filename} ellipse {idx} points not equal 3, got {len(pts)}")
+            ends = [[0,0],[0,0]]
+        else:
+            ends = compute_arc_ends(pts)
+        e = ellipses[idx]
+        arc_shape = {
+            "label": "arc",
+            "points": pts,
+            "params": [e.get("cx",0), e.get("cy",0), e.get("rx",0), e.get("ry",0), e.get("theta",0)],
+            "ends": ends,
+            "group_id": None,
+            "description": "",
+            "difficult": False,
+            "shape_type": "arc",
+            "flags": {},
+            "attributes": {}
+        }
+        new_arc_shapes.append(arc_shape)
+
+    # 保留非 arc shapes
+    remaining_shapes = [s for s in data["shapes"] if s.get("label") != "arc"]
+    data["shapes"] = remaining_shapes + new_arc_shapes
+
+    # 保存 JSON
+    output_json = os.path.join(output_folder, json_file)
+    with open(output_json, "w", encoding="utf-8") as jf:
+        json.dump(data, jf, ensure_ascii=False, indent=2)
+
+    # 复制图片
+    shutil.copy2(img_path, os.path.join(output_folder, filename))
+    print(f"Saved merged JSON and image for: {filename}")
+
+print("\nAll done! Final JSONs and images saved in:", output_folder)

+ 81 - 0
utils/data_process/csv_circle/csv_show.py

@@ -0,0 +1,81 @@
+import os
+import json
+import cv2
+import numpy as np
+import math
+
+# === 配置 ===
+input_folder = r"/data/share/zyh/master_dataset/pokou/251115/output"  # 输入 JSON 和图片
+output_folder = os.path.join(os.path.dirname(input_folder), "show")
+os.makedirs(output_folder, exist_ok=True)
+
+
+# === 工具函数:生成椭圆轨迹点 ===
+def ellipse_points(cx, cy, rx, ry, theta, num=100):
+    """
+    返回椭圆轨迹上的 num 个点
+    theta 单位为弧度
+    """
+    t = np.linspace(0, 2 * np.pi, num)
+    x = rx * np.cos(t)
+    y = ry * np.sin(t)
+    # 旋转
+    xr = x * np.cos(theta) - y * np.sin(theta)
+    yr = x * np.sin(theta) + y * np.cos(theta)
+    # 平移
+    xr += cx
+    yr += cy
+    return np.stack([xr, yr], axis=1).astype(np.int32)
+
+
+# === 遍历输出文件夹 ===
+for file in os.listdir(input_folder):
+    if not file.endswith(".json"):
+        continue
+    json_path = os.path.join(input_folder, file)
+    img_name = file.replace(".json", ".jpg")
+    img_path = os.path.join(input_folder, img_name)
+
+    if not os.path.exists(img_path):
+        print(f"?? Image not found: {img_name}")
+        continue
+
+    img = cv2.imread(img_path)
+    if img is None:
+        continue
+
+    with open(json_path, "r", encoding="utf-8") as jf:
+        data = json.load(jf)
+
+    # === 绘制 shapes ===
+    for shape in data.get("shapes", []):
+        if shape.get("label") == "arc":
+            pts = np.array(shape.get("points", []), dtype=np.int32)
+            ends = np.array(shape.get("ends", []), dtype=np.int32)
+            params = shape.get("params", [0, 0, 0, 0, 0])
+            cx, cy, rx, ry, theta = params
+
+            # 绘制三点
+            for p in pts:
+                cv2.circle(img, (int(p[0]), int(p[1])), 5, (0, 0, 255), -1)  # 红色
+
+            # 绘制端点
+            for p in ends:
+                cv2.circle(img, (int(p[0]), int(p[1])), 7, (0, 255, 0), -1)  # 绿色
+
+            # 绘制椭圆轨迹
+            ep = ellipse_points(cx, cy, rx, ry, theta)
+            for i in range(len(ep) - 1):
+                cv2.line(img, tuple(ep[i]), tuple(ep[i + 1]), (255, 0, 0), 2)  # 蓝色轨迹
+
+        elif shape.get("shape_type") == "line":
+            pts = shape.get("points", [])
+            if len(pts) >= 2:
+                cv2.line(img, tuple(map(int, pts[0])), tuple(map(int, pts[1])), (0, 255, 255), 2)  # 黄色线
+
+    # === 保存结果 ===
+    save_path = os.path.join(output_folder, img_name)
+    cv2.imwrite(save_path, img)
+    print(f"Saved visualization for {img_name} -> {save_path}")
+
+print("\nAll done! Visualizations saved in:", output_folder)

+ 32 - 0
utils/data_process/csv_circle/resice.py

@@ -0,0 +1,32 @@
+import os
+from PIL import Image
+import shutil
+
+# ÊäÈë¡¢Êä³öÎļþ¼Ð
+input_folder = "/data/share/zyh/master_dataset/pokou/251115/csvjson"
+output_folder = "/data/share/zyh/master_dataset/pokou/251115/output"
+os.makedirs(output_folder, exist_ok=True)
+
+# ±éÀúÊäÈëÎļþ¼Ð
+for filename in os.listdir(input_folder):
+    file_path = os.path.join(input_folder, filename)
+    name, ext = os.path.splitext(filename)
+
+    if ext.lower() == ".jpg":
+        # ´ò¿ªÍ¼Æ¬
+        img = Image.open(file_path)
+        w, h = img.size
+
+        # ´´½¨ºÚÉ«±³¾° 2000x2000
+        new_img = Image.new("RGB", (2000, 2000), (0, 0, 0))
+        # ½«Ô­Í¼Õ³Ìùµ½ÉÏ·½
+        new_img.paste(img, (0,0))
+        # ±£´æµ½Êä³öÎļþ¼Ð
+        new_img.save(os.path.join(output_folder, filename))
+
+        # ͬʱ¸´ÖƶÔÓ¦ JSON Îļþ
+        json_file = os.path.join(input_folder, name + ".json")
+        if os.path.exists(json_file):
+            shutil.copy(json_file, os.path.join(output_folder, name + ".json"))
+
+print("´¦ÀíÍê³É£¡")

+ 102 - 0
utils/data_process/d_data_spliter.py

@@ -0,0 +1,102 @@
+"""
+该脚本将源目录下同名的 .json 和 .diff 文件配对,
+并按如下结构将其整理至目标目录:
+
+输出结构如下:
+
+output_dir/
+├── images/
+│   ├── train/
+│   └── val/
+└── labels/
+    ├── train/
+    └── val/
+
+其中:
+- images/train 和 val 存放的是 `.diff` 文件
+- labels/train 和 val 存放的是 `.json` 文件
+"""
+
+import os
+import shutil
+import random
+from pathlib import Path
+
+def organize_data(
+    src_dir,
+    dest_dir,
+    image_extensions=['.tiff'],
+    label_extensions=['.json'],
+    val_ratio=0.2
+):
+    src_dir = Path(src_dir)
+    dest_dir = Path(dest_dir)
+
+    image_dir = dest_dir / 'images'
+    label_dir = dest_dir / 'labels'
+
+    # 创建文件夹结构
+    for split in ['train', 'val']:
+        (image_dir / split).mkdir(parents=True, exist_ok=True)
+        (label_dir / split).mkdir(parents=True, exist_ok=True)
+
+    # 获取所有文件
+    files = list(src_dir.glob('*'))
+    name_to_files = {}
+
+    # 分组:同名文件归为一组
+    for f in files:
+        stem = f.stem
+        name_to_files.setdefault(stem, []).append(f)
+
+    # 筛选出同时包含 label 和 image 的样本
+    paired_samples = []
+    for name, file_group in name_to_files.items():
+        image_file = next((f for f in file_group if f.suffix in image_extensions), None)
+        label_file = next((f for f in file_group if f.suffix in label_extensions), None)
+        if image_file and label_file:
+            paired_samples.append((image_file, label_file))
+        else:
+            print(f"⚠️ Skipping unpaired files for: {name}")
+
+    # 打乱并划分
+    random.shuffle(paired_samples)
+    split_idx = int(len(paired_samples) * (1 - val_ratio))
+    train_samples = paired_samples[:split_idx]
+    val_samples = paired_samples[split_idx:]
+
+    # 拷贝函数
+    def copy_samples(samples, split):
+        for img, lbl in samples:
+            shutil.copy(img, image_dir / split / img.name)
+            shutil.copy(lbl, label_dir / split / lbl.name)
+
+    # 执行拷贝
+    copy_samples(train_samples, 'train')
+    copy_samples(val_samples, 'val')
+
+    print(f"\n✅ Done! Processed {len(paired_samples)} pairs.")
+    print(f"Train: {len(train_samples)}, Val: {len(val_samples)}")
+
+if __name__ == "__main__":
+    # 输入输出目录(可修改)
+    source_dir = r"/data/share/zyh/master_dataset/pokou/251115/output"
+
+    parent_dir = os.path.dirname(source_dir)
+    output_dir = os.path.join(parent_dir, "a_dataset_pokou_mask")
+
+    # 后缀名列表,方便以后扩展其他格式
+    image_exts = ['.tiff','.jpg','.png']
+    label_exts = ['.json']
+
+    organize_data(source_dir, output_dir, image_exts, label_exts)
+
+
+
+
+
+
+
+
+
+

+ 51 - 0
utils/data_process/filter_two+pts/a.py

@@ -0,0 +1,51 @@
+import os
+import json
+import shutil
+
+def filter_single_circle_files(src_folder, dst_folder, img_exts=(".png", ".jpg", ".jpeg")):
+    """
+    ¸´ÖÆËùÓнö°üº¬Ò»¸ö label == 'circle' µÄ JSON Îļþ¼°Æä¶ÔӦͼƬ¡£
+    Ô´Îļþ¼Ð²»Ð޸쬽á¹û±£´æµ½ÐÂÎļþ¼Ð¡£
+    """
+    os.makedirs(dst_folder, exist_ok=True)
+
+    for filename in os.listdir(src_folder):
+        if not filename.lower().endswith(".json"):
+            continue
+
+        json_path = os.path.join(src_folder, filename)
+        try:
+            with open(json_path, "r", encoding="utf-8") as f:
+                data = json.load(f)
+        except Exception as e:
+            print(f"[ERROR] ÎÞ·¨¶ÁÈ¡ {filename}: {e}")
+            continue
+
+        shapes = data.get("shapes", [])
+        circle_count = sum(1 for shape in shapes if shape.get("label") == "circle")
+
+        # ? Ö»±£ÁôÇ¡ºÃÒ»¸ö circle µÄÎļþ
+        if circle_count == 1:
+            base = os.path.splitext(filename)[0]
+
+            # ¸´ÖÆ json
+            dst_json = os.path.join(dst_folder, filename)
+            shutil.copy2(json_path, dst_json)
+
+            # ²éÕÒ²¢¸´ÖÆÍ¬ÃûͼƬ
+            found_img = False
+            for ext in img_exts:
+                img_path = os.path.join(src_folder, base + ext)
+                if os.path.exists(img_path):
+                    shutil.copy2(img_path, os.path.join(dst_folder, base + ext))
+                    found_img = True
+                    break
+
+            print(f"? Copied: {filename} ({circle_count} circle){' + image' if found_img else ''}")
+        else:
+            print(f"Skipped: {filename} ({circle_count} circles)")
+
+if __name__ == "__main__":
+    src_folder = "/data/share/zyh/master_dataset/circle/huayan_circle/251112_251111/total"  # Ô´Îļþ¼Ð
+    dst_folder = "/data/share/zyh/master_dataset/circle/huayan_circle/251112_251111/onecircle"  # Ä¿±êÎļþ¼Ð
+    filter_single_circle_files(src_folder, dst_folder)

+ 70 - 0
utils/data_process/machine/resetjson.py

@@ -0,0 +1,70 @@
+import json
+from pathlib import Path
+from PIL import Image
+
+# ======================
+# Paths Configuration
+# ======================
+JSON_DIR = Path("/data/share/zyh/master_dataset/circle/huayan_circle/251112_251111/day02_machine")
+IMAGE_DIR = Path("/data/share/zyh/master_dataset/circle/huayan_circle/251112_251111/day02_machine")  # source images
+OUTPUT_DIR = JSON_DIR  # overwrite original or use a new folder
+
+# ======================
+# Process each JSON
+# ======================
+for json_file in JSON_DIR.glob("*.json"):
+    with open(json_file, "r") as f:
+        data = json.load(f)
+
+    # Determine corresponding image file
+    stem_name = json_file.stem
+    if stem_name.endswith("_annotations"):
+        stem_name = stem_name.replace("_annotations", "")
+    possible_extensions = [".png", ".jpg", ".jpeg"]
+    img_file = None
+    for ext in possible_extensions:
+        candidate = IMAGE_DIR / f"{stem_name}{ext}"
+        if candidate.exists():
+            img_file = candidate
+            break
+    if img_file is None:
+        print(f"[WARNING] No image found for JSON: {json_file.name}")
+        continue
+
+    # Load image size
+    image = Image.open(img_file)
+    img_width, img_height = image.size
+
+    # Convert each entry to LabelMe shape (without arc)
+    shapes = []
+    for ann in data if isinstance(data, list) else data.get("annotations", []):
+        shape = {
+            "label": ann.get("label", ann.get("class_name", "circle")),
+            "points": ann.get("points", []),
+            "shape_type": "polygon",
+            "flags": {},
+            "xmin": ann.get("xmin", 0),
+            "ymin": ann.get("ymin", 0),
+            "xmax": ann.get("xmax", 0),
+            "ymax": ann.get("ymax", 0)
+        }
+        shapes.append(shape)
+
+    # Create LabelMe JSON
+    labelme_json = {
+        "version": "5.0.1",
+        "flags": {},
+        "shapes": shapes,
+        "imagePath": img_file.name,
+        "imageHeight": img_height,
+        "imageWidth": img_width
+    }
+
+    # Save updated JSON (overwrite or to another folder)
+    output_path = OUTPUT_DIR / json_file.name
+    with open(output_path, "w") as f:
+        json.dump(labelme_json, f, indent=4)
+
+    print(f"Processed JSON saved: {output_path.name}")
+
+print("\n? All JSON files converted to LabelMe format (arc removed).")

+ 69 - 0
utils/data_process/show_feature.py

@@ -0,0 +1,69 @@
+import os
+import torch
+import numpy as np
+import matplotlib.pyplot as plt
+from datetime import datetime
+
+
+def visualize_feature_map(feature_logits,
+                          save_root=r"/home/zhaoyinghan/py_ws/code/pokou/MultiVisionModels/models/line_detect/train_results/feature",
+                          max_saved=5):
+    """
+    Visualize 5 feature maps stacked together. Saves result inside a timestamp folder.
+    Keeps only the last `max_saved` folders.
+
+    Args:
+        feature_logits: Tensor [B, 1, H, W]
+        save_root: root folder to save images
+        max_saved: maximum number of timestamp folders to keep
+    """
+
+    with torch.no_grad():
+        os.makedirs(save_root, exist_ok=True)
+
+        # -----------------------
+        # timestamp folder
+        # -----------------------
+        timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
+        save_dir = os.path.join(save_root, timestamp)
+        os.makedirs(save_dir, exist_ok=True)
+
+        # -----------------------
+        # cleanup old folders
+        # -----------------------
+        all_dirs = [d for d in os.listdir(save_root) if os.path.isdir(os.path.join(save_root, d))]
+        all_dirs.sort()  # °´Ãû×ÖÅÅÐò£¬Ê±¼ä´Á˳Ðò
+        if len(all_dirs) > max_saved:
+            for d in all_dirs[:-max_saved]:
+                try:
+                    full_path = os.path.join(save_root, d)
+                    import shutil
+                    shutil.rmtree(full_path)
+                    print(f"[visualize_feature_map] Removed old folder: {full_path}")
+                except Exception as e:
+                    print(f"[visualize_feature_map] Failed to remove {full_path}: {e}")
+
+        # -----------------------
+        # take first 5 feature maps
+        # -----------------------
+        B = feature_logits.size(0)
+        num = min(5, B)
+        maps = feature_logits[:num, 0, ...].detach().cpu()  # shape (num, H, W)
+
+        # -----------------------
+        # stack & normalize
+        # -----------------------
+        stacked = maps.sum(dim=0).numpy()
+        stacked = (stacked - stacked.min()) / (stacked.max() - stacked.min() + 1e-6)
+
+        # -----------------------
+        # save image
+        # -----------------------
+        save_path = os.path.join(save_dir, "stacked_feature.png")
+        plt.figure(figsize=(6, 6))
+        plt.imshow(stacked, cmap="viridis")
+        plt.axis("off")
+        plt.savefig(save_path, bbox_inches="tight", pad_inches=0)
+        plt.close()
+
+        print(f"[visualize_feature_map] Saved stacked feature ¡ú {save_path}")

+ 59 - 0
utils/data_process/show_prams.py

@@ -0,0 +1,59 @@
+import torch
+import numpy as np
+import inspect
+
+def print_params(*args):
+    """
+    Pretty-print multiple variables, recursively handling
+    list/dict/torch.Tensor/numpy.ndarray/scalar, and automatically
+    print the variable names.
+
+    Usage:
+        print_params(arc_equation, gt_mask_ends, gt_mask_params, arc_pos_matched_idxs)
+    """
+    # Get the calling frame and local variables
+    frame = inspect.currentframe().f_back
+    local_vars = frame.f_locals
+
+    # Try to match args with variable names in caller's scope
+    arg_names = []
+    for arg in args:
+        found = False
+        for name, val in local_vars.items():
+            if val is arg:
+                arg_names.append(name)
+                found = True
+                break
+        if not found:
+            arg_names.append("unknown")
+
+    # Recursive printer
+    def _print(obj, indent=0, max_elements=10):
+        prefix = "  " * indent
+        if isinstance(obj, dict):
+            print(f"{prefix}dict:")
+            for k, v in obj.items():
+                print(f"{prefix}  key: {k}")
+                _print(v, indent + 2, max_elements)
+        elif isinstance(obj, list):
+            print(f"{prefix}list (len={len(obj)}):")
+            for i, v in enumerate(obj):
+                print(f"{prefix}  [{i}]")
+                _print(v, indent + 2, max_elements)
+        elif isinstance(obj, torch.Tensor):
+            if obj.numel() > max_elements:
+                print(f"{prefix}Tensor(shape={tuple(obj.shape)}, dtype={obj.dtype}, device={obj.device}, values={obj.flatten()[:max_elements].tolist()} ...)")
+            else:
+                print(f"{prefix}Tensor(shape={tuple(obj.shape)}, dtype={obj.dtype}, device={obj.device}, values={obj.tolist()})")
+        elif isinstance(obj, np.ndarray):
+            if obj.size > max_elements:
+                print(f"{prefix}ndarray(shape={obj.shape}, dtype={obj.dtype}, values={obj.flatten()[:max_elements]} ...)")
+            else:
+                print(f"{prefix}ndarray(shape={obj.shape}, dtype={obj.dtype}, values={obj.tolist()})")
+        else:
+            print(f"{prefix}{repr(obj)}")
+
+    # Print each variable with its name
+    for name, value in zip(arg_names, args):
+        print(f"\n=== {name} ===")
+        _print(value)

+ 91 - 0
utils/data_process/showarc.py

@@ -0,0 +1,91 @@
+import cv2
+import numpy as np
+import math
+
+
+def draw_arc_on_image(
+    image_path,
+    output_path,
+    params,
+    thickness=2,
+    color=(0, 0, 255)
+):
+    """
+    Draw ellipse arc on an image using given parameters.
+
+    Args:
+        image_path (str): path to the input image.
+        output_path (str): path to save the output image.
+        params (list or tensor): [cx, cy, a, b, theta1, theta2, theta3]
+            cx, cy: center of ellipse
+            a, b: long and short axis (radius, not diameter)
+            theta1, theta2, theta3: angle range (radians)
+        thickness (int): thickness of the arc.
+        color (tuple): BGR color.
+
+    Notes:
+        OpenCV ellipse uses:
+            - center (int, int)
+            - axis lengths (int, int) are *half lengths*
+            - rotation angle in degrees
+            - startAngle, endAngle in degrees
+    """
+
+    # Load image
+    img = cv2.imread(image_path)
+    if img is None:
+        raise FileNotFoundError(f"Image not found: {image_path}")
+
+    cx, cy, a, b, t1, t2, t3 = params
+
+    # Convert to int for OpenCV
+    cx = int(cx)
+    cy = int(cy)
+    a = int(a)       # long axis (radius)
+    b = int(b)       # short axis (radius)
+
+    # Convert radian to degree
+    t1_deg = math.degrees(t1)
+    t2_deg = math.degrees(t2)
+    t3_deg = math.degrees(t3)
+
+    # If you want full ellipse:
+    # cv2.ellipse(img, (cx, cy), (a, b), 0, 0, 360, color, thickness)
+
+    # Draw arc only (theta1 -> theta2)
+    cv2.ellipse(
+        img,
+        (cx, cy),
+        (a, b),
+        0,              # rotation angle of ellipse
+        t1_deg,         # start angle
+        t2_deg,         # end angle
+        color,
+        thickness
+    )
+
+    # Optionally draw second arc ¦È2 ¡ú ¦È3
+    cv2.ellipse(
+        img,
+        (cx, cy),
+        (a, b),
+        0,
+        t2_deg,
+        t3_deg,
+        (0, 255, 0),    # different color for visualization
+        thickness
+    )
+
+    # Save output
+    cv2.imwrite(output_path, img)
+    print(f"[Saved] Arc drawn on image -> {output_path}")
+
+
+# Example usage:
+if __name__ == "__main__":
+    params = [651.5971, 491.6041, 4.5752, 4.3241, 1.1640, 3.0971, 3.1106]
+    draw_arc_on_image(
+        image_path=r"/home/zhaoyinghan/下载/imageData.png",
+        output_path="output_arc.png",
+        params=params
+    )

+ 58 - 0
utils/data_process/zyh/Dilation/expansion.py

@@ -0,0 +1,58 @@
+import os
+import numpy as np
+import imageio.v3 as iio
+from scipy.ndimage import grey_dilation
+
+# 输入输出路径
+input_dir = r"G:\python_ws_g\data\pcd2color_result\depth_tiff"
+output_dir = r"G:\python_ws_g\data\pcd2color_result\dilated_tiff_filled"
+os.makedirs(output_dir, exist_ok=True)
+
+# 获取 .tiff 文件
+image_files = [f for f in os.listdir(input_dir) if f.lower().endswith('.tiff')]
+
+# 定义膨胀核(结构元素)
+kernel = np.ones((3, 3), dtype=bool)  # 小一点更精细填补
+
+def fill_empty_by_dilation(depth, max_iter=50):
+    # 判断有效像素(非0 且 非NaN)
+    mask_valid = (depth > 0) & np.isfinite(depth)
+
+    filled = depth.copy()
+    for i in range(max_iter):
+        prev = filled.copy()
+
+        # 膨胀图像
+        dilated = grey_dilation(filled, footprint=kernel)
+
+        # 只用膨胀值去填空(原来为0的地方)
+        filled = np.where(mask_valid, filled, dilated)
+
+        # 更新 mask(哪些位置现在也有效了)
+        new_mask_valid = (filled > 0) & np.isfinite(filled)
+        if np.array_equal(new_mask_valid, mask_valid):
+            print(f"→ 第{i+1}次迭代:无更多像素可填补,停止")
+            break
+        mask_valid = new_mask_valid
+
+    return filled
+
+for filename in image_files:
+    try:
+        img_path = os.path.join(input_dir, filename)
+        depth = iio.imread(img_path)  # 支持 float32, uint16
+
+        if depth is None:
+            print(f"读取失败: {filename}")
+            continue
+
+        result = fill_empty_by_dilation(depth)
+
+        # 保存结果
+        save_path = os.path.join(output_dir, f"filled_{filename}")
+        iio.imwrite(save_path, result.astype(depth.dtype))
+
+        print(f"填补完成: {filename} → {save_path}")
+
+    except Exception as e:
+        print(f"处理失败: {filename} → {e}")

+ 91 - 0
utils/data_process/zyh/Dilation/val_expansion.py

@@ -0,0 +1,91 @@
+import os
+import json
+import numpy as np
+import matplotlib.pyplot as plt
+import cv2
+from tifffile import tifffile
+
+def draw_lines_on_resized_tiff(json_path, tiff_dir, output_dir=None):
+    with open(json_path, 'r', encoding='utf-8') as f:
+        data = json.load(f)
+
+    if output_dir:
+        os.makedirs(output_dir, exist_ok=True)
+
+    target_width, target_height = 826, 818  # 目标尺寸
+
+    for item in data:
+        original_img_name = item["image"]
+        base_name = os.path.splitext(original_img_name)[0].replace("_color", "_depth")
+        tiff_name = "filled_" + base_name + ".tiff"
+        tiff_path = os.path.join(tiff_dir, tiff_name)
+
+        if not os.path.exists(tiff_path):
+            print(f"[!] TIFF file not found: {tiff_path}")
+            continue
+
+        tiff_np = tifffile.imread(tiff_path)
+        orig_height, orig_width = tiff_np.shape[:2]
+
+        resized_img = cv2.resize(tiff_np, (target_width, target_height), interpolation=cv2.INTER_LINEAR)
+
+        fig, ax = plt.subplots(figsize=(target_width / 100, target_height / 100), dpi=100)
+
+        if resized_img.ndim == 2:
+            ax.imshow(resized_img, cmap='gray')
+        else:
+            ax.imshow(resized_img)
+
+        scale_x = target_width / orig_width
+        scale_y = target_height / orig_height
+
+        for line in item["lines"]:
+            start_2000 = np.array(line["start"])
+            end_2000 = np.array(line["end"])
+
+            scale_line_x = orig_width / 2000
+            scale_line_y = orig_height / 2000
+
+            start_orig = start_2000 * np.array([scale_line_x, scale_line_y])
+            end_orig = end_2000 * np.array([scale_line_x, scale_line_y])
+
+            start = start_orig * np.array([scale_x, scale_y])
+            end = end_orig * np.array([scale_x, scale_y])
+
+            x0, y0 = int(round(start[0])), int(round(start[1]))
+            x1, y1 = int(round(end[0])), int(round(end[1]))
+
+            x0 = np.clip(x0, 0, target_width - 1)
+            y0 = np.clip(y0, 0, target_height - 1)
+            x1 = np.clip(x1, 0, target_width - 1)
+            y1 = np.clip(y1, 0, target_height - 1)
+
+            val_start = resized_img[y0, x0]
+            val_end = resized_img[y1, x1]
+
+            ax.plot([x0, x1], [y0, y1], 'r-', linewidth=1)
+            ax.scatter([x0, x1], [y0, y1], c='blue', s=10)
+            ax.text(x0, y0, f"{val_start}", color='white', fontsize=8,
+                    bbox=dict(facecolor='black', alpha=0.5))
+            ax.text(x1, y1, f"{val_end}", color='white', fontsize=8,
+                    bbox=dict(facecolor='black', alpha=0.5))
+
+        ax.set_title(original_img_name)
+        ax.axis('off')
+
+        if output_dir:
+            save_name = base_name + "_vis_resized.jpg"
+            save_path = os.path.join(output_dir, save_name)
+            plt.savefig(save_path, bbox_inches='tight', dpi=100)
+            plt.close(fig)
+        else:
+            plt.show()
+
+    print("✅ 所有图像处理完成。")
+
+if __name__ == '__main__':
+    json_path = r"G:\python_ws_g\data\pcd2color_result\a_predict_restnet50\predictions.json"
+    tiff_dir = r"G:\python_ws_g\data\pcd2color_result\dilated_tiff_filled"
+    output_dir = r"G:\python_ws_g\data\pcd2color_result\line_vis_resized"
+
+    draw_lines_on_resized_tiff(json_path, tiff_dir, output_dir)

+ 181 - 0
utils/data_process/zyh/circle_rgb/a_4point.py

@@ -0,0 +1,181 @@
+import os
+import json
+import shutil
+import numpy as np
+import cv2
+from scipy.optimize import leastsq
+from tqdm import tqdm
+
+
+def calc_R(x, y, xc, yc):
+    return np.sqrt((x - xc) ** 2 + (y - yc) ** 2)
+
+
+def f(c, x, y):
+    Ri = calc_R(x, y, *c)
+    return Ri - Ri.mean()
+
+def fit_arc_and_create_mask(points, shape, point_step_deg=0.5, radius=2):
+    x_data = points[:, 0]
+    y_data = points[:, 1]
+    center_estimate = np.mean(x_data), np.mean(y_data)
+    try:
+        center, _ = leastsq(f, center_estimate, args=(x_data, y_data))
+        xc, yc = center
+        Ri = calc_R(x_data, y_data, xc, yc)
+        R = Ri.mean()
+
+        h, w = shape
+        if R <= 0 or not np.isfinite(R) or not np.isfinite(xc) or not np.isfinite(yc):
+            raise ValueError("拟合圆非法")
+
+        # 生成完整圆的点
+        circle_angles = np.linspace(0, 2 * np.pi, int(360 / point_step_deg), endpoint=False)
+        full_circle_points = np.stack([
+            xc + R * np.cos(circle_angles),
+            yc + R * np.sin(circle_angles)
+        ], axis=-1).astype(np.float32)
+
+        # 只保留图像内部的点
+        in_bounds_mask = (
+            (full_circle_points[:, 0] >= 0) & (full_circle_points[:, 0] < w) &
+            (full_circle_points[:, 1] >= 0) & (full_circle_points[:, 1] < h)
+        )
+        clipped_points = full_circle_points[in_bounds_mask]
+
+        if len(clipped_points) < 3:
+            print(f"拟合失败使用的三个点为:\n{points}")
+            raise ValueError("拟合圆点全部在图像外")
+
+        return None, clipped_points.tolist()
+
+    except Exception as e:
+        print(f"⚠️ 圆拟合失败:{e}")
+        return None, []
+
+
+def build_shapes_from_points(points, label="circle"):
+    points_np = np.array(points)
+    if points_np.shape[0] < 3:
+        return []
+
+    xs = points_np[:, 0]
+    ys = points_np[:, 1]
+
+    xmin, xmax = xs.min(), xs.max()
+    ymin, ymax = ys.min(), ys.max()
+
+    eps = 1e-3
+
+    # 边界点:每类只能找一个
+    def find_one(points, cond):
+        for pt in points:
+            if cond(pt):
+                return pt
+        return None
+
+    pt_xmin = find_one(points, lambda pt: abs(pt[0] - xmin) < eps)
+    pt_xmax = find_one(points, lambda pt: abs(pt[0] - xmax) < eps)
+    pt_ymin = find_one(points, lambda pt: abs(pt[1] - ymin) < eps)
+    pt_ymax = find_one(points, lambda pt: abs(pt[1] - ymax) < eps)
+
+    boundary_points = []
+    for pt in [pt_xmin, pt_xmax, pt_ymin, pt_ymax]:
+        if pt is not None and pt not in boundary_points:
+            boundary_points.append(pt)
+
+    if len(boundary_points) != 4:
+        print("⚠️ 边界点不足 4 个,跳过该 shape")
+        return []
+
+    shape = {
+        "label": label,
+        "points": boundary_points,
+        "shape_type": "polygon",
+        "flags": {},
+        "xmin": int(xmin),
+        "ymin": int(ymin),
+        "xmax": int(xmax),
+        "ymax": int(ymax)
+    }
+    return [shape]
+
+
+
+
+def process_folder_labelme(input_dir, output_dir, point_step_deg=0.5, radius=2):
+    os.makedirs(output_dir, exist_ok=True)
+
+    for file_name in tqdm(os.listdir(input_dir)):
+        if not file_name.endswith(".json"):
+            continue
+
+        json_path = os.path.join(input_dir, file_name)
+        image_path = json_path.replace(".json", ".jpg")
+        if not os.path.exists(image_path):
+            print(f"图像不存在,跳过:{image_path}")
+            continue
+
+        with open(json_path, "r") as f:
+            label_data = json.load(f)
+
+        arc_points_raw = [item['points'][0] for item in label_data['shapes']
+                          if item.get('label') == 'circle' and len(item.get('points', [])) == 1]
+
+        if len(arc_points_raw) < 3:
+            print(f"{file_name} 中 circle 点数不足 3,跳过")
+            continue
+
+        image = cv2.imread(image_path)
+        h, w = image.shape[:2]
+
+        arc_shapes = []
+        num_groups = len(arc_points_raw) // 3
+
+        for i in range(num_groups):
+            group_points = arc_points_raw[i*3:(i+1)*3]
+            if len(group_points) < 3:
+                print(f"{file_name} 第 {i+1} 组点数不足 3,跳过")
+                continue
+            points = np.array(group_points)
+            try:
+                _, arc_pts = fit_arc_and_create_mask(points, (h, w), point_step_deg, radius)
+                shapes = build_shapes_from_points(arc_pts, label="circle")
+                arc_shapes.extend(shapes)
+            except Exception as e:
+                print(f"{file_name} 第 {i+1} 组拟合失败,跳过。错误:{e}")
+                continue
+
+        if len(arc_shapes) == 0:
+            print(f"{file_name} 没有成功拟合的 circle 区域,跳过")
+            continue
+
+        output_json = {
+            "version": "5.0.1",
+            "flags": {},
+            "shapes": arc_shapes,
+            "imagePath": os.path.basename(image_path),
+            "imageHeight": h,
+            "imageWidth": w
+        }
+
+        base_name = os.path.splitext(os.path.basename(image_path))[0]
+        out_json_path = os.path.join(output_dir, base_name + ".json")
+        out_img_path = os.path.join(output_dir, base_name + ".jpg")
+
+        with open(out_json_path, "w") as f:
+            json.dump(output_json, f, indent=4)
+
+        shutil.copy(image_path, out_img_path)
+
+    print(f"\n✅ 所有 circle 区域已保存为 labelme 格式(图像 + json)到目录:{output_dir}")
+
+
+# ===== 修改为你自己的输入输出路径 =====
+input_dir = r"\\192.168.50.222\share\zyh\data\rgb_453_source_json\dataset_for_guanban"
+output_dir = r"\\192.168.50.222\share\zyh\data\rgb_453_source_json\4point"
+point_step_deg = 0.5
+draw_radius = 2
+
+if __name__ == "__main__":
+    process_folder_labelme(input_dir, output_dir, point_step_deg, draw_radius)

+ 81 - 0
utils/data_process/zyh/circle_rgb/b_pcd2rgbd.py

@@ -0,0 +1,81 @@
+import os
+import cv2
+import numpy as np
+import open3d as o3d
+from glob import glob
+from tifffile import imwrite as tif_write
+
+# 相机内参矩阵
+K = np.array([
+    [1.30449e3, 0,         5.2602e2],
+    [0,         1.30449e3, 1.07432e3],
+    [0,         0,         1]
+])
+fx, fy = K[0, 0], K[1, 1]
+cx, cy = K[0, 2], K[1, 2]
+
+# 图像尺寸
+height, width = 2000, 2000
+
+def pointscloud2colorimg(points):
+    color_image = np.zeros((height, width, 3), dtype=np.float32)
+    for point in points:
+        X, Y, Z, r, g, b = point
+        if Z > 0:
+            u = int((X * fx) / Z + cx)
+            v = int((Y * fy) / Z + cy)
+            if 0 <= u < width and 0 <= v < height:
+                color_image[v, u] = [r * 255, g * 255, b * 255]
+    return color_image
+
+def pointscloud2depthmap(points):
+    depth_map = np.zeros((height, width), dtype=np.float32)
+    for point in points:
+        X, Y, Z, *_ = point
+        if Z > 0:
+            u = int((X * fx) / Z + cx)
+            v = int((Y * fy) / Z + cy)
+            if 0 <= u < width and 0 <= v < height:
+                depth_map[v, u] = Z
+    return depth_map
+
+def process_pcd_file(pcd_path, rgbd_dir):
+    filename = os.path.splitext(os.path.basename(pcd_path))[0]
+    print(f"Processing: {filename}")
+
+    pcd = o3d.io.read_point_cloud(pcd_path)
+    points = np.asarray(pcd.points)
+    colors = np.asarray(pcd.colors)
+
+    if points.shape[0] == 0 or colors.shape[0] == 0:
+        print(f"Skipping empty or invalid PCD: {filename}")
+        return
+
+    points_colored = np.hstack((points, colors))
+
+    color_img = pointscloud2colorimg(points_colored)
+    depth_map = pointscloud2depthmap(points_colored)
+
+    # RGB 转 uint8,Depth 保持 float32
+    color_img_u8 = np.clip(color_img, 0, 255).astype(np.uint8)
+    depth_map_mm = depth_map.astype(np.float32)
+
+    # 合并为 4 通道 RGBD 图像(float32)
+    rgbd = np.concatenate((color_img_u8, depth_map_mm[..., np.newaxis]), axis=2)
+    tif_write(os.path.join(rgbd_dir, f"{filename}_rgbd.tiff"), rgbd, photometric='rgb')
+
+    print(f"Saved RGBD TIFF for {filename}")
+
+input_dir = r"G:\python_ws_g\data\pcd"
+output_base = os.path.dirname(input_dir)
+
+rgbd_dir = os.path.join(output_base, "rgbd_tiff")
+os.makedirs(rgbd_dir, exist_ok=True)
+
+# 获取 PCD 文件
+pcd_files = glob(os.path.join(input_dir, "*.pcd"))
+print(f"Found {len(pcd_files)} PCD files.")
+
+# 批量处理
+for pcd_path in pcd_files:
+    process_pcd_file(pcd_path, rgbd_dir)

+ 35 - 0
utils/data_process/zyh/circle_rgb/c_matchtiffjson.py

@@ -0,0 +1,35 @@
+import os
+import shutil
+
+# 输入文件夹路径:只需修改这三行
+json_folder = r"G:\python_ws_g\data\manycircle"
+tiff_folder = r"G:\python_ws_g\data\rgbd_tiff"
+output_folder = r"G:\python_ws_g\data\tiff_json"
+
+# 创建输出文件夹(如不存在)
+os.makedirs(output_folder, exist_ok=True)
+
+# 提取 json 文件中的标识前缀(用于匹配)
+json_files = [f for f in os.listdir(json_folder) if f.endswith('_color.json')]
+json_keys = {f.replace('_color.json', ''): f for f in json_files}
+
+# 遍历 tiff 文件,尝试匹配 json
+matched_count = 0
+for tiff_name in os.listdir(tiff_folder):
+    if not tiff_name.endswith('.tiff'):
+        continue
+    if '_rgbd.tiff' not in tiff_name:
+        continue
+
+    key = tiff_name.replace('_rgbd.tiff', '')
+    if key in json_keys:
+        # 匹配成功,复制两份文件到目标目录
+        json_src = os.path.join(json_folder, json_keys[key])
+        tiff_src = os.path.join(tiff_folder, tiff_name)
+
+        shutil.copy(json_src, os.path.join(output_folder, json_keys[key]))
+        shutil.copy(tiff_src, os.path.join(output_folder, tiff_name))
+        print(f"匹配并复制:{json_keys[key]} <-> {tiff_name}")
+        matched_count += 1
+
+print(f"\n✅ 共复制 {matched_count} 对文件到:{output_folder}")

+ 16 - 0
utils/data_process/zyh/circle_rgb/d_rename.py

@@ -0,0 +1,16 @@
+import os
+
+# 设置你的目标文件夹路径
+folder_path = r'G:\python_ws_g\data\tiff_json'  # 例如 r'G:\data\LaserData'
+
+# 遍历该文件夹下的所有文件
+for filename in os.listdir(folder_path):
+    if filename.endswith('_color.json'):
+        # 构造旧文件路径和新文件路径
+        old_path = os.path.join(folder_path, filename)
+        new_filename = filename.replace('_color.json', '_rgbd.json')
+        new_path = os.path.join(folder_path, new_filename)
+
+        # 重命名文件
+        os.rename(old_path, new_path)
+        print(f'Renamed: {filename} -> {new_filename}')

+ 102 - 0
utils/data_process/zyh/circle_rgb/e_data_spliter.py

@@ -0,0 +1,102 @@
+"""
+该脚本将源目录下同名的 .json 和 .diff 文件配对,
+并按如下结构将其整理至目标目录:
+
+输出结构如下:
+
+output_dir/
+├── images/
+│   ├── train/
+│   └── val/
+└── labels/
+    ├── train/
+    └── val/
+
+其中:
+- images/train 和 val 存放的是 `.diff` 文件
+- labels/train 和 val 存放的是 `.json` 文件
+"""
+
+import os
+import shutil
+import random
+from pathlib import Path
+
+def organize_data(
+    src_dir,
+    dest_dir,
+    image_extensions=['.tiff'],
+    label_extensions=['.json'],
+    val_ratio=0.2
+):
+    src_dir = Path(src_dir)
+    dest_dir = Path(dest_dir)
+
+    image_dir = dest_dir / 'images'
+    label_dir = dest_dir / 'labels'
+
+    # 创建文件夹结构
+    for split in ['train', 'val']:
+        (image_dir / split).mkdir(parents=True, exist_ok=True)
+        (label_dir / split).mkdir(parents=True, exist_ok=True)
+
+    # 获取所有文件
+    files = list(src_dir.glob('*'))
+    name_to_files = {}
+
+    # 分组:同名文件归为一组
+    for f in files:
+        stem = f.stem
+        name_to_files.setdefault(stem, []).append(f)
+
+    # 筛选出同时包含 label 和 image 的样本
+    paired_samples = []
+    for name, file_group in name_to_files.items():
+        image_file = next((f for f in file_group if f.suffix in image_extensions), None)
+        label_file = next((f for f in file_group if f.suffix in label_extensions), None)
+        if image_file and label_file:
+            paired_samples.append((image_file, label_file))
+        else:
+            print(f"⚠️ Skipping unpaired files for: {name}")
+
+    # 打乱并划分
+    random.shuffle(paired_samples)
+    split_idx = int(len(paired_samples) * (1 - val_ratio))
+    train_samples = paired_samples[:split_idx]
+    val_samples = paired_samples[split_idx:]
+
+    # 拷贝函数
+    def copy_samples(samples, split):
+        for img, lbl in samples:
+            shutil.copy(img, image_dir / split / img.name)
+            shutil.copy(lbl, label_dir / split / lbl.name)
+
+    # 执行拷贝
+    copy_samples(train_samples, 'train')
+    copy_samples(val_samples, 'val')
+
+    print(f"\n✅ Done! Processed {len(paired_samples)} pairs.")
+    print(f"Train: {len(train_samples)}, Val: {len(val_samples)}")
+
+if __name__ == "__main__":
+    # 输入输出目录(可修改)
+    source_dir = r"\\192.168.50.222\share\zyh\data\rgb_tuoyuan_4point\source"
+
+    parent_dir = os.path.dirname(source_dir)
+    output_dir = os.path.join(parent_dir, "a_dataset")
+
+    # 后缀名列表,方便以后扩展其他格式
+    image_exts = ['.tiff','.jpg']
+    label_exts = ['.json']
+
+    organize_data(source_dir, output_dir, image_exts, label_exts)
+
+
+
+
+
+
+
+
+
+

+ 73 - 0
utils/data_process/zyh/circle_rgb/show.py

@@ -0,0 +1,73 @@
+import os
+import json
+import cv2
+import numpy as np
+
+input_folder = r"G:\python_ws_g\data\manycircle"
+output_folder = r"G:\python_ws_g\data\arcshow"
+
+MAX_WIDTH = 1280
+MAX_HEIGHT = 720
+
+def resize_to_fit(image, max_width=MAX_WIDTH, max_height=MAX_HEIGHT):
+    h, w = image.shape[:2]
+    scale = min(max_width / w, max_height / h, 1.0)
+    new_size = (int(w * scale), int(h * scale))
+    resized_image = cv2.resize(image, new_size, interpolation=cv2.INTER_NEAREST)  # 改成最近邻防止圆点模糊
+    return resized_image
+
+def fit_circle(points):
+    """最小二乘法拟合圆: 返回 (cx, cy, r)"""
+    x = points[:, 0]
+    y = points[:, 1]
+    A = np.c_[2*x, 2*y, np.ones(len(points))]
+    b = x**2 + y**2
+    sol, _, _, _ = np.linalg.lstsq(A, b, rcond=None)
+    cx, cy, c = sol
+    r = np.sqrt(c + cx**2 + cy**2)
+    return cx, cy, r
+
+def visualize_and_save(input_folder, output_folder):
+    os.makedirs(output_folder, exist_ok=True)
+    files = [f for f in os.listdir(input_folder) if f.endswith('.json')]
+    files.sort()
+
+    for json_file in files:
+        json_path = os.path.join(input_folder, json_file)
+        with open(json_path, 'r', encoding='utf-8') as f:
+            data = json.load(f)
+
+        image_path = os.path.join(input_folder, data["imagePath"])
+        if not os.path.exists(image_path):
+            print(f"图像不存在:{image_path}")
+            continue
+
+        image = cv2.imread(image_path)
+
+        for shape in data["shapes"]:
+            label = shape["label"]
+            points = np.array(shape["points"], dtype=np.float32)
+
+            # 画关键点
+            for (x, y) in points:
+                cv2.circle(image, (int(x), int(y)), radius=3, color=(0, 255, 0), thickness=-1)
+
+            # 拟合圆
+            if points.shape[0] == 4:
+                cx, cy, r = fit_circle(points)
+                cv2.circle(image, (int(cx), int(cy)), int(r), (255, 0, 0), 2)  # 蓝色圆
+                cv2.circle(image, (int(cx), int(cy)), 3, (0, 0, 255), -1)      # 圆心红点
+
+            # 写标签
+            x0, y0 = int(points[0][0]), int(points[0][1])
+            cv2.putText(image, label, (x0, y0 - 5),
+                        cv2.FONT_HERSHEY_SIMPLEX, 0.6, (0, 255, 0), 1)
+
+        resized_image = resize_to_fit(image)
+        base_name = os.path.splitext(json_file)[0]
+        save_path = os.path.join(output_folder, base_name + ".jpg")
+        cv2.imwrite(save_path, resized_image)
+        print(f"保存图片: {save_path}")
+
+if __name__ == "__main__":
+    visualize_and_save(input_folder, output_folder)

+ 94 - 0
utils/data_process/zyh/circle_rgb/via/ajson.py

@@ -0,0 +1,94 @@
+import json
+import os
+import cv2
+import shutil
+
+# 输入文件夹
+json_folder = r"\\192.168.50.222\share\zyh\data\via\via_json"
+image_folder = r"\\192.168.50.222\share\zqy\colorphoto_for_guanban"
+output_folder = r"\\192.168.50.222\share\zyh\data\rgb_tuoyuan_4point"
+
+os.makedirs(output_folder, exist_ok=True)
+
+for json_file in os.listdir(json_folder):
+    if not json_file.lower().endswith(".json"):
+        continue
+
+    json_path = os.path.join(json_folder, json_file)
+
+    with open(json_path, "r", encoding="utf-8") as f:
+        data = json.load(f)
+
+    img_metadata = data.get("_via_img_metadata", {})
+
+    for img_info in img_metadata.values():
+        filename = img_info.get("filename")
+        regions = img_info.get("regions", [])
+
+        # 跳过没有椭圆的图片
+        ellipses = [r for r in regions if r.get("shape_attributes", {}).get("name") == "ellipse"]
+        if not ellipses:
+            continue
+
+        # 找图片路径
+        img_path = os.path.join(image_folder, filename)
+        if not os.path.exists(img_path):
+            print(f"图片未找到: {filename}")
+            continue
+
+        # 读取图片尺寸
+        img = cv2.imread(img_path)
+        if img is None:
+            print(f"图片无法读取: {filename}")
+            continue
+        height, width = img.shape[:2]
+
+        # 构造输出 JSON
+        output_data = {
+            "version": "5.0.1",
+            "flags": {},
+            "shapes": [],
+            "imagePath": filename,
+            "imageHeight": height,
+            "imageWidth": width
+        }
+
+        for e in ellipses:
+            cx = e["shape_attributes"]["cx"]
+            cy = e["shape_attributes"]["cy"]
+            rx = e["shape_attributes"]["rx"]
+            ry = e["shape_attributes"]["ry"]
+
+            # 四个端点
+            left = [cx - rx, cy]
+            right = [cx + rx, cy]
+            top = [cx, cy - ry]
+            bottom = [cx, cy + ry]
+
+            xmin = min(left[0], right[0], top[0], bottom[0])
+            xmax = max(left[0], right[0], top[0], bottom[0])
+            ymin = min(left[1], right[1], top[1], bottom[1])
+            ymax = max(left[1], right[1], top[1], bottom[1])
+
+            shape_data = {
+                "label": "circle",
+                "points": [left, right, top, bottom],
+                "shape_type": "polygon",
+                "flags": {},
+                "xmin": int(xmin),
+                "ymin": int(ymin),
+                "xmax": int(xmax),
+                "ymax": int(ymax)
+            }
+            output_data["shapes"].append(shape_data)
+
+        # 保存 JSON
+        json_output_path = os.path.join(output_folder, filename.replace(".jpg", ".json"))
+        with open(json_output_path, "w", encoding="utf-8") as out_f:
+            json.dump(output_data, out_f, ensure_ascii=False, indent=4)
+
+        # 复制图片到输出文件夹
+        img_output_path = os.path.join(output_folder, filename)
+        shutil.copy(img_path, img_output_path)
+
+        print(f"已生成: {json_output_path} 和 {img_output_path}")

+ 63 - 0
utils/data_process/zyh/circle_rgb/via/show.py

@@ -0,0 +1,63 @@
+import json
+import os
+import cv2
+import shutil
+import math
+
+# 输入文件夹
+json_folder = r"\\192.168.50.222\share\zyh\data\via\via_json"
+image_folder = r"\\192.168.50.222\share\zqy\colorphoto_for_guanban"
+output_folder = r"\\192.168.50.222\share\zyh\data\rgb_tuoyuan_draw"
+os.makedirs(output_folder, exist_ok=True)
+
+for json_file in os.listdir(json_folder):
+    if not json_file.lower().endswith(".json"):
+        continue
+
+    json_path = os.path.join(json_folder, json_file)
+
+    with open(json_path, "r", encoding="utf-8") as f:
+        data = json.load(f)
+
+    img_metadata = data.get("_via_img_metadata", {})
+
+    for img_info in img_metadata.values():
+        filename = img_info.get("filename")
+        regions = img_info.get("regions", [])
+
+        # 提取椭圆
+        ellipses = [r for r in regions if r.get("shape_attributes", {}).get("name") == "ellipse"]
+        if not ellipses:
+            continue
+
+        # 找图片路径
+        img_path = os.path.join(image_folder, filename)
+        if not os.path.exists(img_path):
+            print(f"图片未找到: {filename}")
+            continue
+
+        img = cv2.imread(img_path)
+        if img is None:
+            print(f"图片无法读取: {filename}")
+            continue
+
+        # 绘制椭圆
+        for e in ellipses:
+            cx = e["shape_attributes"]["cx"]
+            cy = e["shape_attributes"]["cy"]
+            rx = e["shape_attributes"]["rx"]
+            ry = e["shape_attributes"]["ry"]
+            theta = e["shape_attributes"].get("theta", 0)  # 弧度
+
+            # OpenCV ellipse 角度需要度数,顺时针
+            angle_deg = -math.degrees(theta)
+            center = (int(cx), int(cy))
+            axes = (int(rx), int(ry))
+
+            cv2.ellipse(img, center, axes, angle_deg, 0, 360, (0, 255, 0), 2)
+
+        # 保存画好的图片
+        img_output_path = os.path.join(output_folder, filename)
+        cv2.imwrite(img_output_path, img)
+
+        print(f"已生成带椭圆标注的图片: {img_output_path}")

+ 102 - 0
utils/data_process/zyh/d_data_spliter.py

@@ -0,0 +1,102 @@
+"""
+该脚本将源目录下同名的 .json 和 .diff 文件配对,
+并按如下结构将其整理至目标目录:
+
+输出结构如下:
+
+output_dir/
+├── images/
+│   ├── train/
+│   └── val/
+└── labels/
+    ├── train/
+    └── val/
+
+其中:
+- images/train 和 val 存放的是 `.diff` 文件
+- labels/train 和 val 存放的是 `.json` 文件
+"""
+
+import os
+import shutil
+import random
+from pathlib import Path
+
+def organize_data(
+    src_dir,
+    dest_dir,
+    image_extensions=['.tiff'],
+    label_extensions=['.json'],
+    val_ratio=0.2
+):
+    src_dir = Path(src_dir)
+    dest_dir = Path(dest_dir)
+
+    image_dir = dest_dir / 'images'
+    label_dir = dest_dir / 'labels'
+
+    # 创建文件夹结构
+    for split in ['train', 'val']:
+        (image_dir / split).mkdir(parents=True, exist_ok=True)
+        (label_dir / split).mkdir(parents=True, exist_ok=True)
+
+    # 获取所有文件
+    files = list(src_dir.glob('*'))
+    name_to_files = {}
+
+    # 分组:同名文件归为一组
+    for f in files:
+        stem = f.stem
+        name_to_files.setdefault(stem, []).append(f)
+
+    # 筛选出同时包含 label 和 image 的样本
+    paired_samples = []
+    for name, file_group in name_to_files.items():
+        image_file = next((f for f in file_group if f.suffix in image_extensions), None)
+        label_file = next((f for f in file_group if f.suffix in label_extensions), None)
+        if image_file and label_file:
+            paired_samples.append((image_file, label_file))
+        else:
+            print(f"⚠️ Skipping unpaired files for: {name}")
+
+    # 打乱并划分
+    random.shuffle(paired_samples)
+    split_idx = int(len(paired_samples) * (1 - val_ratio))
+    train_samples = paired_samples[:split_idx]
+    val_samples = paired_samples[split_idx:]
+
+    # 拷贝函数
+    def copy_samples(samples, split):
+        for img, lbl in samples:
+            shutil.copy(img, image_dir / split / img.name)
+            shutil.copy(lbl, label_dir / split / lbl.name)
+
+    # 执行拷贝
+    copy_samples(train_samples, 'train')
+    copy_samples(val_samples, 'val')
+
+    print(f"\n✅ Done! Processed {len(paired_samples)} pairs.")
+    print(f"Train: {len(train_samples)}, Val: {len(val_samples)}")
+
+if __name__ == "__main__":
+    # 输入输出目录(可修改)
+    source_dir = r"G:\python_ws_g\data\color"
+
+    parent_dir = os.path.dirname(source_dir)
+    output_dir = os.path.join(parent_dir, "a_dataset")
+
+    # 后缀名列表,方便以后扩展其他格式
+    image_exts = ['.tiff','.jpg']
+    label_exts = ['.json']
+
+    organize_data(source_dir, output_dir, image_exts, label_exts)
+
+
+
+
+
+
+
+
+
+

+ 100 - 0
utils/data_process/zyh/dataset_util.py

@@ -0,0 +1,100 @@
+"""
+该脚本将源目录下同名的 .json 和 .diff 文件配对,
+并按如下结构将其整理至目标目录:
+
+输出结构如下:
+
+output_dir/
+├── images/
+│   ├── train/
+│   └── val/
+└── labels/
+    ├── train/
+    └── val/
+
+其中:
+- images/train 和 val 存放的是 `.diff` 文件
+- labels/train 和 val 存放的是 `.json` 文件
+"""
+
+import os
+import shutil
+import random
+from pathlib import Path
+
+def organize_data(
+    src_dir,
+    dest_dir,
+    image_extensions=['.tiff'],
+    label_extensions=['.json'],
+    val_ratio=0.2
+):
+    src_dir = Path(src_dir)
+    dest_dir = Path(dest_dir)
+
+    image_dir = dest_dir / 'images'
+    label_dir = dest_dir / 'labels'
+
+    # 创建文件夹结构
+    for split in ['train', 'val']:
+        (image_dir / split).mkdir(parents=True, exist_ok=True)
+        (label_dir / split).mkdir(parents=True, exist_ok=True)
+
+    # 获取所有文件
+    files = list(src_dir.glob('*'))
+    name_to_files = {}
+
+    # 分组:同名文件归为一组
+    for f in files:
+        stem = f.stem
+        name_to_files.setdefault(stem, []).append(f)
+
+    # 筛选出同时包含 label 和 image 的样本
+    paired_samples = []
+    for name, file_group in name_to_files.items():
+        image_file = next((f for f in file_group if f.suffix in image_extensions), None)
+        label_file = next((f for f in file_group if f.suffix in label_extensions), None)
+        if image_file and label_file:
+            paired_samples.append((image_file, label_file))
+        else:
+            print(f"⚠️ Skipping unpaired files for: {name}")
+
+    # 打乱并划分
+    random.shuffle(paired_samples)
+    split_idx = int(len(paired_samples) * (1 - val_ratio))
+    train_samples = paired_samples[:split_idx]
+    val_samples = paired_samples[split_idx:]
+
+    # 拷贝函数
+    def copy_samples(samples, split):
+        for img, lbl in samples:
+            shutil.copy(img, image_dir / split / img.name)
+            shutil.copy(lbl, label_dir / split / lbl.name)
+
+    # 执行拷贝
+    copy_samples(train_samples, 'train')
+    copy_samples(val_samples, 'val')
+
+    print(f"\n✅ Done! Processed {len(paired_samples)} pairs.")
+    print(f"Train: {len(train_samples)}, Val: {len(val_samples)}")
+
+if __name__ == "__main__":
+    # 输入输出目录(可修改)
+    source_dir = r"\\192.168.50.222\share\zyh\512\init\target"
+    output_dir = r"\\192.168.50.222\share\zyh\512\init\last"
+
+    # 后缀名列表,方便以后扩展其他格式
+    image_exts = ['.tiff']
+    label_exts = ['.json']
+
+    organize_data(source_dir, output_dir, image_exts, label_exts)
+
+
+
+
+
+
+
+
+
+

+ 80 - 0
utils/data_process/zyh/match_pcd_new_jpg.py

@@ -0,0 +1,80 @@
+import os
+import shutil
+
+def match_pcd_meta_tiff(image_json_folder, pcd_folder, depth_tiff_folder,
+                        output_pcd_folder, output_meta_folder, output_tiff_folder):
+    os.makedirs(output_pcd_folder, exist_ok=True)
+    os.makedirs(output_meta_folder, exist_ok=True)
+    os.makedirs(output_tiff_folder, exist_ok=True)
+
+    # PCD 前缀集合(去掉后缀 .pcd)
+    pcd_prefixes = {
+        os.path.splitext(f)[0] for f in os.listdir(pcd_folder) if f.lower().endswith('.pcd')
+    }
+
+    # image_json_folder 中的 jpg 和 json 文件(带 _color 后缀)
+    all_files = os.listdir(image_json_folder)
+    color_jpgs = [f for f in all_files if f.lower().endswith('_color.jpg')]
+    jsons = [f for f in all_files if f.lower().endswith('_color.json')]
+
+    # 生成前缀映射,去掉后缀 _color.jpg / _color.json
+    jpg_prefix_map = {f[:-10]: f for f in color_jpgs}    # '_color.jpg' 长度是10
+    json_prefix_map = {f[:-11]: f for f in jsons}        # '_color.json' 长度是11
+
+    # depth_tiff_folder 中的深度图文件,文件名以 _depth.tiff 或 _depth.tif 结尾
+    tiff_files = [f for f in os.listdir(depth_tiff_folder) if f.lower().endswith(('.tiff', '.tif'))]
+    # 去掉 _depth.tiff 后缀得到前缀
+    tiff_prefix_map = {}
+    for f in tiff_files:
+        if f.lower().endswith('_depth.tiff'):
+            prefix = f[:-11]  # 去掉 '_depth.tiff' (11个字符)
+        elif f.lower().endswith('_depth.tif'):
+            prefix = f[:-10]  # 去掉 '_depth.tif' (10个字符)
+        else:
+            continue
+        tiff_prefix_map[prefix] = f
+
+    # 找到同时有 pcd、jpg、json、tiff 的 prefix
+    matched_prefixes = []
+    for prefix in pcd_prefixes:
+        if (prefix in jpg_prefix_map and
+            prefix in json_prefix_map and
+            prefix in tiff_prefix_map):
+            matched_prefixes.append(prefix)
+
+    print(f"共发现 {len(pcd_prefixes)} 个 pcd 文件")
+    print(f"共发现 {len(color_jpgs)} 个 jpg (_color.jpg) 文件")
+    print(f"共发现 {len(jsons)} 个 json (_color.json) 文件")
+    print(f"共发现 {len(tiff_files)} 个 tiff (_depth.tiff/.tif) 文件")
+    print(f"匹配到 {len(matched_prefixes)} 组文件")
+
+    # 复制匹配文件到对应输出文件夹
+    for prefix in matched_prefixes:
+        pcd_src = os.path.join(pcd_folder, prefix + '.pcd')
+        jpg_src = os.path.join(image_json_folder, jpg_prefix_map[prefix])
+        json_src = os.path.join(image_json_folder, json_prefix_map[prefix])
+        tiff_src = os.path.join(depth_tiff_folder, tiff_prefix_map[prefix])
+
+        pcd_dst = os.path.join(output_pcd_folder, prefix + '.pcd')
+        jpg_dst = os.path.join(output_meta_folder, jpg_prefix_map[prefix])
+        json_dst = os.path.join(output_meta_folder, json_prefix_map[prefix])
+        tiff_dst = os.path.join(output_tiff_folder, tiff_prefix_map[prefix])
+
+        shutil.copy2(pcd_src, pcd_dst)
+        shutil.copy2(jpg_src, jpg_dst)
+        shutil.copy2(json_src, json_dst)
+        shutil.copy2(tiff_src, tiff_dst)
+
+    print(f"复制完成,匹配了 {len(matched_prefixes)} 组文件。")
+
+if __name__ == "__main__":
+    image_json_folder = r"\\192.168.50.222\share\zyh\5月彩色钢板数据汇总\513-515数据汇总\0525保存xyz信息的jpg+0516修订后的json"
+    pcd_folder = r"\\192.168.50.222\share\zyh\5月彩色钢板数据汇总\513-515数据汇总\pcd"
+    depth_tiff_folder = r"\\192.168.50.222\share\zyh\5月彩色钢板数据汇总\513-515数据汇总\depth_tiff"
+
+    output_pcd_folder = r"\\192.168.50.222\share\zyh\5月彩色钢板数据汇总\513-515数据汇总\匹配后的pcd"
+    output_meta_folder = r"\\192.168.50.222\share\zyh\5月彩色钢板数据汇总\513-515数据汇总\匹配后的json_jpg"
+    output_tiff_folder = r"\\192.168.50.222\share\zyh\5月彩色钢板数据汇总\513-515数据汇总\匹配后的depth_tiff"
+
+    match_pcd_meta_tiff(image_json_folder, pcd_folder, depth_tiff_folder,
+                        output_pcd_folder, output_meta_folder, output_tiff_folder)

+ 44 - 0
utils/data_process/zyh/matchold_newjpg.py

@@ -0,0 +1,44 @@
+import os
+import shutil
+
+
+def match_and_copy(folder_a, folder_b, output_folder):
+    os.makedirs(output_folder, exist_ok=True)
+
+    # 获取文件名(去掉后缀)
+    json_names_a = {os.path.splitext(f)[0] for f in os.listdir(folder_a) if f.lower().endswith('.json')}
+    jpg_names_b = {os.path.splitext(f)[0] for f in os.listdir(folder_b) if f.lower().endswith('.jpg')}
+
+    # 找出匹配上的名字
+    common_names = json_names_a.intersection(jpg_names_b)
+
+    # 找出未匹配上的名字(json 有,jpg 没有)
+    unmatched_names = json_names_a - jpg_names_b
+
+    for name in common_names:
+        jpg_src_path = os.path.join(folder_b, name + '.jpg')  # jpg 来自 B
+        json_src_path = os.path.join(folder_a, name + '.json')  # json 来自 A
+
+        jpg_dst_path = os.path.join(output_folder, name + '.jpg')
+        json_dst_path = os.path.join(output_folder, name + '.json')
+
+        # 复制文件
+        shutil.copy2(jpg_src_path, jpg_dst_path)
+        shutil.copy2(json_src_path, json_dst_path)
+
+    print(f'完成匹配,复制了 {len(common_names)} 对文件到 {output_folder}')
+
+    if unmatched_names:
+        print("\n以下文件在文件夹A中存在 json,但文件夹B中没有对应 jpg:")
+        for name in sorted(unmatched_names):
+            print(f"- {name}.json")
+    else:
+        print("\n所有 json 文件都成功匹配到了 jpg 文件。")
+
+
+if __name__ == "__main__":
+    folder_a = r"\\192.168.50.222\share\zyh\5月彩色钢板数据汇总\513-515数据汇总\0516修订彩色钢板"  # 含json和jpg
+    folder_b = r"\\192.168.50.222\share\zyh\5月彩色钢板数据汇总\513-515数据汇总\pcd2color_result\color_jpg"  # 只有jpg
+    output_folder = r"\\192.168.50.222\share\zyh\5月彩色钢板数据汇总\513-515数据汇总\0525保存xyz信息的jpg+0516修订后的json"
+
+    match_and_copy(folder_a, folder_b, output_folder)

+ 85 - 0
utils/data_process/zyh/pcd2clolor_init.py

@@ -0,0 +1,85 @@
+import os
+import cv2
+import numpy as np
+import open3d as o3d
+from glob import glob
+from tifffile import imwrite as tif_write
+
+# 相机内参矩阵
+K = np.array([
+    [1.30449e3, 0,         5.2602e2],
+    [0,         1.30449e3, 1.07432e3],
+    [0,         0,         1]
+])
+fx, fy = K[0, 0], K[1, 1]
+cx, cy = K[0, 2], K[1, 2]
+
+# 目标图像尺寸
+height, width = 2000, 2000
+
+def pointscloud2colorimg(points):
+    color_image = np.zeros((height, width, 3), dtype=np.float32)
+    for point in points:
+        X, Y, Z, r, g, b = point
+        if Z > 0:
+            u = int((X * fx) / Z + cx)
+            v = int((Y * fy) / Z + cy)
+            if 0 <= u < width and 0 <= v < height:
+                color_image[v, u] = [r * 255, g * 255, b * 255]
+    return color_image
+
+def pointscloud2depthmap(points):
+    depth_map = np.zeros((height, width), dtype=np.float32)
+    for point in points:
+        X, Y, Z, *_ = point
+        if Z > 0:
+            u = int((X * fx) / Z + cx)
+            v = int((Y * fy) / Z + cy)
+            if 0 <= u < width and 0 <= v < height:
+                depth_map[v, u] = Z
+    return depth_map
+
+def process_pcd_file(pcd_path, color_dir, depth_dir):
+    filename = os.path.splitext(os.path.basename(pcd_path))[0]
+    print(f"Processing: {filename}")
+
+    pcd = o3d.io.read_point_cloud(pcd_path)
+    points = np.asarray(pcd.points) #xyz
+    colors = np.asarray(pcd.colors) #rgb
+
+    if points.shape[0] == 0 or colors.shape[0] == 0:
+        print(f"Skipping empty or invalid PCD: {filename}")
+        return
+
+    points_colored = np.hstack((points, colors))
+
+    color_img = pointscloud2colorimg(points_colored)
+    depth_map = pointscloud2depthmap(points_colored)
+
+    # 保存图像
+    color_img_u8 = np.clip(color_img, 0, 255).astype(np.uint8)
+    cv2.imwrite(os.path.join(color_dir, f"{filename}_color.jpg"), color_img_u8)
+
+    depth_map_mm = (depth_map ).astype(np.float32)
+
+    tif_write(os.path.join(depth_dir, f"{filename}_depth.tiff"), depth_map_mm)
+
+
+    print(f"Saved color and depth images for {filename}")
+
+# 设置输入输出路径
+input_dir = r"G:\python_ws_g\data\2025-05-22-11-40-20_LaserData_Hi221518(1)"
+output_base = r"G:\python_ws_g\data\pcd2color_result"
+color_dir = os.path.join(output_base, "color_jpg")
+depth_dir = os.path.join(output_base, "depth_tiff")
+
+os.makedirs(color_dir, exist_ok=True)
+os.makedirs(depth_dir, exist_ok=True)
+
+# 获取所有 PCD 文件
+pcd_files = glob(os.path.join(input_dir, "*.pcd"))
+print(f"Found {len(pcd_files)} PCD files.")
+
+# 批量处理
+for pcd_path in pcd_files:
+    process_pcd_file(pcd_path, color_dir, depth_dir)

+ 78 - 0
utils/data_process/zyh/pcd2color.py

@@ -0,0 +1,78 @@
+import os
+import cv2
+import numpy as np
+import open3d as o3d
+from glob import glob
+from collections import defaultdict
+
+# 相机内参矩阵
+K = np.array([
+    [1.30449e3, 0,         5.2602e2],
+    [0,         1.30449e3, 1.07432e3],
+    [0,         0,         1]
+])
+fx, fy = K[0, 0], K[1, 1]
+cx, cy = K[0, 2], K[1, 2]
+
+# 目标图像尺寸
+height, width = 2000, 2000
+
+
+def pointscloud2colorimg(points):
+    color_image = np.zeros((height, width, 3), dtype=np.float32)
+    point_count = np.zeros((height, width), dtype=np.int32)  # 统计每个像素覆盖的点数
+
+    for point in points:
+        X, Y, Z, r, g, b = point
+        if Z > 0:
+            u = int((X * fx) / Z + cx)
+            v = int((Y * fy) / Z + cy)
+            if 0 <= u < width and 0 <= v < height:
+                color_image[v, u] = [r * 255, g * 255, b * 255]
+                point_count[v, u] += 1  # 该像素多了一个点
+
+    return color_image, point_count
+
+
+def process_pcd_file(pcd_path, color_dir):
+    filename = os.path.splitext(os.path.basename(pcd_path))[0]
+    print(f"Processing: {filename}")
+
+    pcd = o3d.io.read_point_cloud(pcd_path)
+    points = np.asarray(pcd.points)  # xyz
+    colors = np.asarray(pcd.colors)  # rgb
+
+    if points.shape[0] == 0 or colors.shape[0] == 0:
+        print(f"Skipping empty or invalid PCD: {filename}")
+        return
+
+    points_colored = np.hstack((points, colors))
+
+    color_img, point_count = pointscloud2colorimg(points_colored)
+
+    # 保存图像
+    color_img_u8 = np.clip(color_img, 0, 255).astype(np.uint8)
+    cv2.imwrite(os.path.join(color_dir, f"{filename}_color.jpg"), color_img_u8)
+
+    # 统计覆盖情况
+    num_multi_cover = np.sum(point_count > 1)      # 有多少像素点被多个点覆盖
+    total_points = np.sum(point_count)            # 总点数
+    max_cover = np.max(point_count)               # 单个像素最多的点数
+
+    print(f"{filename}: 总点数={total_points}, 被多个点覆盖的像素数={num_multi_cover}, 单像素最多点数={max_cover}")
+
+
+# 设置输入输出路径
+input_dir = r"\\192.168.50.222\share\zqy\dianyun0731"
+output_base = r"G:\python_ws_g\data\color"
+color_dir = os.path.join(output_base, "color_jpg")
+
+os.makedirs(color_dir, exist_ok=True)
+
+# 获取所有 PCD 文件
+pcd_files = glob(os.path.join(input_dir, "*.pcd"))
+print(f"Found {len(pcd_files)} PCD files.")
+
+# 批量处理
+for pcd_path in pcd_files:
+    process_pcd_file(pcd_path, color_dir)

+ 84 - 0
utils/data_process/zyh/pcd2tiff.py

@@ -0,0 +1,84 @@
+import os
+import numpy as np
+import open3d as o3d
+from tifffile import tifffile
+
+# 相机内参矩阵
+K = np.array([
+    [1.30449e3, 0,         5.2602e2],
+    [0,         1.30449e3, 1.07432e3],
+    [0,         0,         1]
+])
+fx, fy = K[0, 0], K[1, 1]
+cx, cy = K[0, 2], K[1, 2]
+
+# 目标图像尺寸
+height, width = 2000, 2000
+
+def pointscloud2depthmap(points):
+    """将点云投影为深度图"""
+    point_image = np.zeros((height, width, 3), dtype=np.float32)
+    for point in points:
+        X, Y, Z = point
+        if Z > 0:
+            u = int((X * fx) / Z + cx)
+            v = int((Y * fy) / Z + cy)
+            if 0 <= u < width and 0 <= v < height:
+                point_image[v, u, :] = point
+    return point_image
+
+def process_pcd_folder(folder_path, output_folder, mode='f32'):
+    """
+    批量处理PCD文件,生成深度图并保存为TIFF格式。
+    mode: 'f32' | 'int8' | 'all32'
+    """
+    if not os.path.exists(output_folder):
+        os.makedirs(output_folder)
+
+    for filename in os.listdir(folder_path):
+        if filename.lower().endswith('.pcd'):
+            full_path = os.path.join(folder_path, filename)
+            print(f"Processing: {full_path}")
+
+            try:
+                # 读取点云
+                pcd = o3d.io.read_point_cloud(full_path)
+                points = np.asarray(pcd.points)
+
+                # 投影为图像
+                point_image = pointscloud2depthmap(points)
+
+                if mode == 'f32':
+                    depth_map = point_image[:, :, 2].astype(np.float32)
+                elif mode == 'int8':
+                    depth_map = point_image[:, :, 2].astype(np.int8)
+                elif mode == 'all32':
+                    depth_map = point_image.astype(np.float32)
+                else:
+                    raise ValueError(f"Unsupported mode: {mode}")
+
+                # 输出路径
+                output_name = os.path.splitext(filename)[0] + ".tiff"
+                output_path = os.path.join(output_folder, output_name)
+
+                # 保存TIFF
+                tifffile.imwrite(output_path, depth_map)
+                print(f"Saved depth map to: {output_path}")
+            except Exception as e:
+                print(f"Error processing {filename}: {e}")
+
+def main():
+    input_folder = r"G:\python_ws_g\data\2025-05-22-11-40-20_LaserData_Hi221518(1)"
+
+    # 定义各输出路径
+    output_f32 = r"G:\python_ws_g\data\process_pcd_folder_f32"
+    output_int8 = r"G:\python_ws_g\data\process_pcd_folder_int8"
+    output_all32 = r"G:\python_ws_g\data\process_pcd_folder_all32"
+
+    # 执行处理
+    process_pcd_folder(input_folder, output_f32, mode='f32')
+    process_pcd_folder(input_folder, output_int8, mode='int8')
+    process_pcd_folder(input_folder, output_all32, mode='all32')
+
+if __name__ == '__main__':
+    main()

+ 119 - 0
utils/data_process/zyh/predict/pridict.py

@@ -0,0 +1,119 @@
+import os
+import cv2
+import torch
+import json
+import numpy as np
+from PIL import Image
+import matplotlib.pyplot as plt
+from torchvision import transforms
+from models.line_detect.line_net import linenet_resnet50_fpn
+from models.wirenet.postprocess import postprocess
+
+device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
+
+def load_best_model(model, save_path, device):
+    if os.path.exists(save_path):
+        checkpoint = torch.load(save_path, map_location=device)
+        model.load_state_dict(checkpoint['model_state_dict'])
+    else:
+        print(f"No saved model found at {save_path}")
+    return model
+
+def show_line_and_box(img_tensor, pred, img_name="", save_path=None):
+    im = img_tensor.permute(1, 2, 0).cpu().numpy()
+    boxes = pred[0]['boxes'].cpu().numpy()
+    lines = pred[-1]['wires']['lines'][0].cpu().numpy() / 128 * 512
+    scores = pred[-1]['wires']['score'].cpu().numpy()[0]
+
+    # 后处理线
+    diag = (im.shape[0] ** 2 + im.shape[1] ** 2) ** 0.5
+    lines, line_scores = postprocess(lines, scores, diag * 0.01, 0, False)
+
+    fig, ax = plt.subplots(figsize=(10, 10))
+    ax.imshow(im)
+
+    for box in boxes:
+        x0, y0, x1, y1 = box
+        ax.add_patch(plt.Rectangle((x0, y0), x1 - x0, y1 - y0,
+                                   fill=False, edgecolor='lime', linewidth=1))
+
+    for (a, b) in lines:
+        ax.plot([a[1], b[1]], [a[0], b[0]], c='red', linewidth=1)
+        ax.scatter([a[1], b[1]], [a[0], b[0]], c='red', s=3)
+
+    if save_path:
+        fig.savefig(save_path, bbox_inches='tight')
+        plt.close(fig)
+    else:
+        plt.show()
+
+    # 返回线段信息
+    line_list = []
+    for (a, b) in lines:
+        line_list.append({
+            "start": [round(float(a[1]), 2), round(float(a[0]), 2)],  # [x, y]
+            "end": [round(float(b[1]), 2), round(float(b[0]), 2)]
+        })
+    return line_list
+
+def predict_and_save(img_path, model, output_path):
+    image = Image.open(img_path).convert("RGB")
+    transform = transforms.ToTensor()
+    img_tensor = transform(image)
+    im = img_tensor.permute(1, 2, 0)
+
+    # Resize to 512x512
+    if im.shape != (512, 512, 3):
+        im = cv2.resize(im.cpu().numpy(), (512, 512), interpolation=cv2.INTER_LINEAR)
+        img_tensor = torch.tensor(im).permute(2, 0, 1)
+
+    img_tensor = img_tensor.to(device)
+
+    with torch.no_grad():
+        predictions = model([img_tensor])
+
+    img_name = os.path.basename(img_path)
+    lines = show_line_and_box(img_tensor, predictions, img_name=img_name, save_path=output_path)
+
+    return {
+        "image": img_name,
+        "lines": lines
+    }
+
+def process_folder(input_dir, output_dir, model, pt_path):
+    model = load_best_model(model, pt_path, device)
+    model.eval()
+
+    os.makedirs(output_dir, exist_ok=True)
+
+    supported_exts = ('.jpg', '.jpeg', '.png')
+    img_list = [f for f in os.listdir(input_dir) if f.lower().endswith(supported_exts)]
+
+    all_results = []
+
+    for img_name in img_list:
+        img_path = os.path.join(input_dir, img_name)
+        save_path = os.path.join(output_dir, os.path.splitext(img_name)[0] + ".jpg")
+        print(f"Processing {img_path}...")
+        result = predict_and_save(img_path, model, save_path)
+        all_results.append(result)
+
+    # 保存为 JSON 文件
+    json_path = os.path.join(output_dir, "predictions.json")
+    with open(json_path, 'w', encoding='utf-8') as f:
+        json.dump(all_results, f, indent=2, ensure_ascii=False)
+
+    print(f"处理完成,结果保存在: {json_path}")
+
+def main():
+    model = linenet_resnet50_fpn().to(device)
+
+    input_dir = r"G:\python_ws_g\data\pcd2color_result\color_jpg"
+    parent_dir = os.path.dirname(input_dir)
+    output_dir = os.path.join(parent_dir, "a_predict_restnet50")
+    pt_path = r"G:\python_ws_g\code\mulitivision汇总\转tiff\MultiVisionModels\weight\best_val.pth"
+
+    process_folder(input_dir, output_dir, model, pt_path)
+
+if __name__ == '__main__':
+    main()

+ 89 - 0
utils/data_process/zyh/rgbd/1fastrgbd.py

@@ -0,0 +1,89 @@
+import os
+import numpy as np
+import open3d as o3d
+from glob import glob
+from tifffile import imwrite as tif_write
+
+# 相机内参矩阵
+K = np.array([
+    [1.30449e3, 0,         5.2602e2],
+    [0,         1.30449e3, 1.07432e3],
+    [0,         0,         1]
+])
+fx, fy = K[0, 0], K[1, 1]
+cx, cy = K[0, 2], K[1, 2]
+
+# 图像尺寸
+height, width = 2000, 2000
+
+def compute_uv_mask(points):
+    """
+    计算 u, v 像素坐标和有效 mask
+    """
+    X, Y, Z = points[:, 0], points[:, 1], points[:, 2]
+    valid = Z > 0
+    X, Y, Z = X[valid], Y[valid], Z[valid]
+
+    u = ((X * fx) / Z + cx).astype(np.int32)
+    v = ((Y * fy) / Z + cy).astype(np.int32)
+
+    mask = (u >= 0) & (u < width) & (v >= 0) & (v < height)
+
+    return u[mask], v[mask], Z[mask], valid, mask
+
+def build_rgb_image(points, u, v, valid_mask):
+    r, g, b = points[:, 3], points[:, 4], points[:, 5]
+    r, g, b = r[valid_mask], g[valid_mask], b[valid_mask]
+
+    color_image = np.zeros((height, width, 3), dtype=np.uint8)
+    color_image[v, u, 0] = (r * 255).astype(np.uint8)
+    color_image[v, u, 1] = (g * 255).astype(np.uint8)
+    color_image[v, u, 2] = (b * 255).astype(np.uint8)
+    return color_image
+
+def build_depth_map(u, v, Z):
+    depth_map = np.zeros((height, width), dtype=np.float32)
+    depth_map[v, u] = Z
+    return depth_map
+
+def process_pcd_file(pcd_path, rgbd_dir):
+    filename = os.path.splitext(os.path.basename(pcd_path))[0]
+    print(f"Processing: {filename}")
+
+    pcd = o3d.io.read_point_cloud(pcd_path)
+    points = np.asarray(pcd.points)
+    colors = np.asarray(pcd.colors)
+
+    if points.shape[0] == 0 or colors.shape[0] == 0:
+        print(f"Skipping empty or invalid PCD: {filename}")
+        return
+
+    points_colored = np.hstack((points, colors))  # Nx6
+
+    # 一次性计算 u, v 和 mask
+    u, v, Z, valid_z_mask, final_mask = compute_uv_mask(points_colored)
+
+    # 生成 RGB 和 depth 图像
+    color_img = build_rgb_image(points_colored[valid_z_mask], u, v, final_mask)
+    depth_map = build_depth_map(u, v, Z)
+
+    # 合并为 RGBD
+    rgbd = np.zeros((height, width, 4), dtype=np.float32)
+    rgbd[:, :, 0:3] = color_img.astype(np.float32)
+    rgbd[:, :, 3] = depth_map
+
+    # 保存 tiff
+    tif_write(os.path.join(rgbd_dir, f"{filename}_rgbd.tiff"), rgbd, photometric='rgb')
+    print(f"Saved RGBD TIFF for {filename}")
+
+if __name__ == "__main__":
+    input_dir = r"\\192.168.50.222\share2\zyh\data\rgbd_line\dianyun0731"
+    output_base = os.path.dirname(input_dir)
+    rgbd_dir = os.path.join(output_base, "rgbd_tiff")
+    os.makedirs(rgbd_dir, exist_ok=True)
+
+    pcd_files = glob(os.path.join(input_dir, "*.pcd"))
+    print(f"Found {len(pcd_files)} PCD files.")
+
+    for pcd_path in pcd_files:
+        process_pcd_file(pcd_path, rgbd_dir)

+ 29 - 0
utils/data_process/zyh/rgbd/2rename.py

@@ -0,0 +1,29 @@
+import os
+
+# 设置你的目标文件夹路径
+folder_path = r'G:\python_ws_g\data\color'  # 例如 r'G:\data\LaserData'
+
+# # 遍历该文件夹下的所有文件
+# for filename in os.listdir(folder_path):
+#     if filename.endswith('_color.json'):
+#         # 构造旧文件路径和新文件路径
+#         old_path = os.path.join(folder_path, filename)
+#         new_filename = filename.replace('_color.json', '_rgbd.json')
+#         new_path = os.path.join(folder_path, new_filename)
+#
+#         # 重命名文件
+#         os.rename(old_path, new_path)
+#         print(f'Renamed: {filename} -> {new_filename}')
+
+# 遍历该文件夹下的所有文件
+for filename in os.listdir(folder_path):
+    if filename.endswith('_rgbd.json'):
+        # 构造旧文件路径和新文件路径
+        old_path = os.path.join(folder_path, filename)
+        # new_filename = filename.replace('_color.json', '_rgbd.json')
+        new_filename = filename.replace( '_rgbd.json','_color.json')
+        new_path = os.path.join(folder_path, new_filename)
+
+        # 重命名文件
+        os.rename(old_path, new_path)
+        print(f'Renamed: {filename} -> {new_filename}')

+ 156 - 0
utils/data_process/zyh/rgbd/circle/a_circle.py

@@ -0,0 +1,156 @@
+import os
+import json
+import shutil
+import numpy as np
+import cv2
+from scipy.optimize import leastsq
+from tqdm import tqdm
+
+
+def calc_R(x, y, xc, yc):
+    return np.sqrt((x - xc) ** 2 + (y - yc) ** 2)
+
+
+def f(c, x, y):
+    Ri = calc_R(x, y, *c)
+    return Ri - Ri.mean()
+
+def fit_arc_and_create_mask(points, shape, point_step_deg=0.5, radius=2):
+    x_data = points[:, 0]
+    y_data = points[:, 1]
+    center_estimate = np.mean(x_data), np.mean(y_data)
+    try:
+        center, _ = leastsq(f, center_estimate, args=(x_data, y_data))
+        xc, yc = center
+        Ri = calc_R(x_data, y_data, xc, yc)
+        R = Ri.mean()
+
+        h, w = shape
+        if R <= 0 or not np.isfinite(R) or not np.isfinite(xc) or not np.isfinite(yc):
+            raise ValueError("拟合圆非法")
+
+        # 生成完整圆的点
+        circle_angles = np.linspace(0, 2 * np.pi, int(360 / point_step_deg), endpoint=False)
+        full_circle_points = np.stack([
+            xc + R * np.cos(circle_angles),
+            yc + R * np.sin(circle_angles)
+        ], axis=-1).astype(np.float32)
+
+        # 只保留图像内部的点
+        in_bounds_mask = (
+            (full_circle_points[:, 0] >= 0) & (full_circle_points[:, 0] < w) &
+            (full_circle_points[:, 1] >= 0) & (full_circle_points[:, 1] < h)
+        )
+        clipped_points = full_circle_points[in_bounds_mask]
+
+        if len(clipped_points) < 3:
+            print(f"拟合失败使用的三个点为:\n{points}")
+            raise ValueError("拟合圆点全部在图像外")
+
+        return None, clipped_points.tolist()
+
+    except Exception as e:
+        print(f"⚠️ 圆拟合失败:{e}")
+        return None, []
+
+
+
+
+
+def build_shapes_from_points(points, label="circle"):
+    points_np = np.array(points)
+    if points_np.shape[0] < 3:
+        return []
+
+    xs = points_np[:, 0]
+    ys = points_np[:, 1]
+
+    shape = {
+        "label": label,
+        "points": points,
+        "shape_type": "polygon",
+        "flags": {},
+        "xmin": int(xs.min()),
+        "ymin": int(ys.min()),
+        "xmax": int(xs.max()),
+        "ymax": int(ys.max())
+    }
+    return [shape]
+
+
+def process_folder_labelme(input_dir, output_dir, point_step_deg=0.5, radius=2):
+    os.makedirs(output_dir, exist_ok=True)
+
+    for file_name in tqdm(os.listdir(input_dir)):
+        if not file_name.endswith(".json"):
+            continue
+
+        json_path = os.path.join(input_dir, file_name)
+        image_path = json_path.replace(".json", ".jpg")
+        if not os.path.exists(image_path):
+            print(f"图像不存在,跳过:{image_path}")
+            continue
+
+        with open(json_path, "r") as f:
+            label_data = json.load(f)
+
+        arc_points_raw = [item['points'][0] for item in label_data['shapes']
+                          if item.get('label') == 'circle' and len(item.get('points', [])) == 1]
+
+        if len(arc_points_raw) < 3:
+            print(f"{file_name} 中 circle 点数不足 3,跳过")
+            continue
+
+        image = cv2.imread(image_path)
+        h, w = image.shape[:2]
+
+        arc_shapes = []
+        num_groups = len(arc_points_raw) // 3
+
+        for i in range(num_groups):
+            group_points = arc_points_raw[i*3:(i+1)*3]
+            if len(group_points) < 3:
+                print(f"{file_name} 第 {i+1} 组点数不足 3,跳过")
+                continue
+            points = np.array(group_points)
+            try:
+                _, arc_pts = fit_arc_and_create_mask(points, (h, w), point_step_deg, radius)
+                shapes = build_shapes_from_points(arc_pts, label="circle")
+                arc_shapes.extend(shapes)
+            except Exception as e:
+                print(f"{file_name} 第 {i+1} 组拟合失败,跳过。错误:{e}")
+                continue
+
+        if len(arc_shapes) == 0:
+            print(f"{file_name} 没有成功拟合的 circle 区域,跳过")
+            continue
+
+        output_json = {
+            "version": "5.0.1",
+            "flags": {},
+            "shapes": arc_shapes,
+            "imagePath": os.path.basename(image_path),
+            "imageHeight": h,
+            "imageWidth": w
+        }
+
+        base_name = os.path.splitext(os.path.basename(image_path))[0]
+        out_json_path = os.path.join(output_dir, base_name + ".json")
+        out_img_path = os.path.join(output_dir, base_name + ".jpg")
+
+        with open(out_json_path, "w") as f:
+            json.dump(output_json, f, indent=4)
+
+        shutil.copy(image_path, out_img_path)
+
+    print(f"\n✅ 所有 circle 区域已保存为 labelme 格式(图像 + json)到目录:{output_dir}")
+
+
+# ===== 修改为你自己的输入输出路径 =====
+input_dir = r"G:\python_ws_g\data\dataset_for_guanban"
+output_dir = r"G:\python_ws_g\data\manycircle"
+point_step_deg = 0.5
+draw_radius = 2
+
+if __name__ == "__main__":
+    process_folder_labelme(input_dir, output_dir, point_step_deg, draw_radius)

+ 81 - 0
utils/data_process/zyh/rgbd/circle/b_pcd2rgbd.py

@@ -0,0 +1,81 @@
+import os
+import cv2
+import numpy as np
+import open3d as o3d
+from glob import glob
+from tifffile import imwrite as tif_write
+
+# 相机内参矩阵
+K = np.array([
+    [1.30449e3, 0,         5.2602e2],
+    [0,         1.30449e3, 1.07432e3],
+    [0,         0,         1]
+])
+fx, fy = K[0, 0], K[1, 1]
+cx, cy = K[0, 2], K[1, 2]
+
+# 图像尺寸
+height, width = 2000, 2000
+
+def pointscloud2colorimg(points):
+    color_image = np.zeros((height, width, 3), dtype=np.float32)
+    for point in points:
+        X, Y, Z, r, g, b = point
+        if Z > 0:
+            u = int((X * fx) / Z + cx)
+            v = int((Y * fy) / Z + cy)
+            if 0 <= u < width and 0 <= v < height:
+                color_image[v, u] = [r * 255, g * 255, b * 255]
+    return color_image
+
+def pointscloud2depthmap(points):
+    depth_map = np.zeros((height, width), dtype=np.float32)
+    for point in points:
+        X, Y, Z, *_ = point
+        if Z > 0:
+            u = int((X * fx) / Z + cx)
+            v = int((Y * fy) / Z + cy)
+            if 0 <= u < width and 0 <= v < height:
+                depth_map[v, u] = Z
+    return depth_map
+
+def process_pcd_file(pcd_path, rgbd_dir):
+    filename = os.path.splitext(os.path.basename(pcd_path))[0]
+    print(f"Processing: {filename}")
+
+    pcd = o3d.io.read_point_cloud(pcd_path)
+    points = np.asarray(pcd.points)
+    colors = np.asarray(pcd.colors)
+
+    if points.shape[0] == 0 or colors.shape[0] == 0:
+        print(f"Skipping empty or invalid PCD: {filename}")
+        return
+
+    points_colored = np.hstack((points, colors))
+
+    color_img = pointscloud2colorimg(points_colored)
+    depth_map = pointscloud2depthmap(points_colored)
+
+    # RGB 转 uint8,Depth 保持 float32
+    color_img_u8 = np.clip(color_img, 0, 255).astype(np.uint8)
+    depth_map_mm = depth_map.astype(np.float32)
+
+    # 合并为 4 通道 RGBD 图像(float32)
+    rgbd = np.concatenate((color_img_u8, depth_map_mm[..., np.newaxis]), axis=2)
+    tif_write(os.path.join(rgbd_dir, f"{filename}_rgbd.tiff"), rgbd, photometric='rgb')
+
+    print(f"Saved RGBD TIFF for {filename}")
+
+input_dir = r"G:\python_ws_g\data\pcd"
+output_base = os.path.dirname(input_dir)
+
+rgbd_dir = os.path.join(output_base, "rgbd_tiff")
+os.makedirs(rgbd_dir, exist_ok=True)
+
+# 获取 PCD 文件
+pcd_files = glob(os.path.join(input_dir, "*.pcd"))
+print(f"Found {len(pcd_files)} PCD files.")
+
+# 批量处理
+for pcd_path in pcd_files:
+    process_pcd_file(pcd_path, rgbd_dir)

+ 35 - 0
utils/data_process/zyh/rgbd/circle/c_matchtiffjson.py

@@ -0,0 +1,35 @@
+import os
+import shutil
+
+# 输入文件夹路径:只需修改这三行
+json_folder = r"G:\python_ws_g\data\manycircle"
+tiff_folder = r"G:\python_ws_g\data\rgbd_tiff"
+output_folder = r"G:\python_ws_g\data\tiff_json"
+
+# 创建输出文件夹(如不存在)
+os.makedirs(output_folder, exist_ok=True)
+
+# 提取 json 文件中的标识前缀(用于匹配)
+json_files = [f for f in os.listdir(json_folder) if f.endswith('_color.json')]
+json_keys = {f.replace('_color.json', ''): f for f in json_files}
+
+# 遍历 tiff 文件,尝试匹配 json
+matched_count = 0
+for tiff_name in os.listdir(tiff_folder):
+    if not tiff_name.endswith('.tiff'):
+        continue
+    if '_rgbd.tiff' not in tiff_name:
+        continue
+
+    key = tiff_name.replace('_rgbd.tiff', '')
+    if key in json_keys:
+        # 匹配成功,复制两份文件到目标目录
+        json_src = os.path.join(json_folder, json_keys[key])
+        tiff_src = os.path.join(tiff_folder, tiff_name)
+
+        shutil.copy(json_src, os.path.join(output_folder, json_keys[key]))
+        shutil.copy(tiff_src, os.path.join(output_folder, tiff_name))
+        print(f"匹配并复制:{json_keys[key]} <-> {tiff_name}")
+        matched_count += 1
+
+print(f"\n✅ 共复制 {matched_count} 对文件到:{output_folder}")

+ 16 - 0
utils/data_process/zyh/rgbd/circle/d_rename.py

@@ -0,0 +1,16 @@
+import os
+
+# 设置你的目标文件夹路径
+folder_path = r'G:\python_ws_g\data\tiff_json'  # 例如 r'G:\data\LaserData'
+
+# 遍历该文件夹下的所有文件
+for filename in os.listdir(folder_path):
+    if filename.endswith('_color.json'):
+        # 构造旧文件路径和新文件路径
+        old_path = os.path.join(folder_path, filename)
+        new_filename = filename.replace('_color.json', '_rgbd.json')
+        new_path = os.path.join(folder_path, new_filename)
+
+        # 重命名文件
+        os.rename(old_path, new_path)
+        print(f'Renamed: {filename} -> {new_filename}')

+ 102 - 0
utils/data_process/zyh/rgbd/circle/e_data_spliter.py

@@ -0,0 +1,102 @@
+"""
+该脚本将源目录下同名的 .json 和 .diff 文件配对,
+并按如下结构将其整理至目标目录:
+
+输出结构如下:
+
+output_dir/
+├── images/
+│   ├── train/
+│   └── val/
+└── labels/
+    ├── train/
+    └── val/
+
+其中:
+- images/train 和 val 存放的是 `.diff` 文件
+- labels/train 和 val 存放的是 `.json` 文件
+"""
+
+import os
+import shutil
+import random
+from pathlib import Path
+
+def organize_data(
+    src_dir,
+    dest_dir,
+    image_extensions=['.tiff'],
+    label_extensions=['.json'],
+    val_ratio=0.2
+):
+    src_dir = Path(src_dir)
+    dest_dir = Path(dest_dir)
+
+    image_dir = dest_dir / 'images'
+    label_dir = dest_dir / 'labels'
+
+    # 创建文件夹结构
+    for split in ['train', 'val']:
+        (image_dir / split).mkdir(parents=True, exist_ok=True)
+        (label_dir / split).mkdir(parents=True, exist_ok=True)
+
+    # 获取所有文件
+    files = list(src_dir.glob('*'))
+    name_to_files = {}
+
+    # 分组:同名文件归为一组
+    for f in files:
+        stem = f.stem
+        name_to_files.setdefault(stem, []).append(f)
+
+    # 筛选出同时包含 label 和 image 的样本
+    paired_samples = []
+    for name, file_group in name_to_files.items():
+        image_file = next((f for f in file_group if f.suffix in image_extensions), None)
+        label_file = next((f for f in file_group if f.suffix in label_extensions), None)
+        if image_file and label_file:
+            paired_samples.append((image_file, label_file))
+        else:
+            print(f"⚠️ Skipping unpaired files for: {name}")
+
+    # 打乱并划分
+    random.shuffle(paired_samples)
+    split_idx = int(len(paired_samples) * (1 - val_ratio))
+    train_samples = paired_samples[:split_idx]
+    val_samples = paired_samples[split_idx:]
+
+    # 拷贝函数
+    def copy_samples(samples, split):
+        for img, lbl in samples:
+            shutil.copy(img, image_dir / split / img.name)
+            shutil.copy(lbl, label_dir / split / lbl.name)
+
+    # 执行拷贝
+    copy_samples(train_samples, 'train')
+    copy_samples(val_samples, 'val')
+
+    print(f"\n✅ Done! Processed {len(paired_samples)} pairs.")
+    print(f"Train: {len(train_samples)}, Val: {len(val_samples)}")
+
+if __name__ == "__main__":
+    # 输入输出目录(可修改)
+    source_dir = r"G:\python_ws_g\data\manycircle"
+
+    parent_dir = os.path.dirname(source_dir)
+    output_dir = os.path.join(parent_dir, "a_dataset")
+
+    # 后缀名列表,方便以后扩展其他格式
+    image_exts = ['.tiff','.jpg']
+    label_exts = ['.json']
+
+    organize_data(source_dir, output_dir, image_exts, label_exts)
+
+
+
+
+
+
+
+
+
+

+ 58 - 0
utils/data_process/zyh/rgbd/circle/show.py

@@ -0,0 +1,58 @@
+import os
+import json
+import cv2
+import numpy as np
+
+input_folder = r"G:\python_ws_g\data\manycircle"   # 你的 JSON + 图片输入文件夹
+output_folder = r"G:\python_ws_g\data\arcshow"  # 保存带标注图片的文件夹
+# input_folder = r"G:\python_ws_g\data\xiao\out"   # 你的 JSON + 图片输入文件夹
+# output_folder = r"G:\python_ws_g\data\xiao\arcshow"  # 保存带标注图片的文件夹
+
+MAX_WIDTH = 1280
+MAX_HEIGHT = 720
+
+def resize_to_fit(image, max_width=MAX_WIDTH, max_height=MAX_HEIGHT):
+    h, w = image.shape[:2]
+    scale = min(max_width / w, max_height / h, 1.0)  # 只缩小不放大
+    new_size = (int(w * scale), int(h * scale))
+    resized_image = cv2.resize(image, new_size, interpolation=cv2.INTER_AREA)
+    return resized_image
+
+
+def visualize_and_save(input_folder, output_folder):
+    os.makedirs(output_folder, exist_ok=True)
+
+    files = [f for f in os.listdir(input_folder) if f.endswith('.json')]
+    files.sort()
+
+    for json_file in files:
+        json_path = os.path.join(input_folder, json_file)
+        with open(json_path, 'r', encoding='utf-8') as f:
+            data = json.load(f)
+
+        image_path = os.path.join(input_folder, data["imagePath"])
+        if not os.path.exists(image_path):
+            print(f"图像不存在:{image_path}")
+            continue
+
+        image = cv2.imread(image_path)
+
+        for shape in data["shapes"]:
+            label = shape["label"]
+            points = shape["points"]
+            for pt in points:
+                x, y = int(pt[0]), int(pt[1])
+                cv2.circle(image, (x, y), radius=3, color=(0, 255, 0), thickness=-1)
+
+            x0, y0 = int(points[0][0]), int(points[0][1])
+            cv2.putText(image, label, (x0, y0 - 5), cv2.FONT_HERSHEY_SIMPLEX, 0.6, (0, 255, 0), 1)
+
+        resized_image = resize_to_fit(image)
+
+        base_name = os.path.splitext(json_file)[0]
+        save_path = os.path.join(output_folder, base_name + ".jpg")
+        cv2.imwrite(save_path, resized_image)
+        print(f"保存图片: {save_path}")
+
+if __name__ == "__main__":
+    visualize_and_save(input_folder, output_folder)

+ 139 - 0
utils/data_process/zyh/rgbd/mask_choumi/mask.py

@@ -0,0 +1,139 @@
+import os
+import json
+import shutil
+import numpy as np
+import cv2
+from scipy.optimize import leastsq
+from tqdm import tqdm
+
+
+def calc_R(x, y, xc, yc):
+    return np.sqrt((x - xc) ** 2 + (y - yc) ** 2)
+
+
+def f(c, x, y):
+    Ri = calc_R(x, y, *c)
+    return Ri - Ri.mean()
+
+
+def fit_arc_and_create_mask(points, shape, point_step_deg=0.5, radius=2):
+    x_data = points[:, 0]
+    y_data = points[:, 1]
+    center_estimate = np.mean(x_data), np.mean(y_data)
+    center, _ = leastsq(f, center_estimate, args=(x_data, y_data))
+    xc, yc = center
+    Ri = calc_R(x_data, y_data, xc, yc)
+    R = Ri.mean()
+
+    # 极角
+    angles = np.arctan2(points[:, 1] - yc, points[:, 0] - xc)
+    start_angle = np.min(angles)
+    end_angle = np.max(angles)
+
+    # 确保角度顺序正确
+    if end_angle - start_angle > np.pi:
+        start_angle, end_angle = end_angle, start_angle + 2 * np.pi
+
+    arc_angles = np.arange(start_angle, end_angle, np.deg2rad(point_step_deg))
+    arc_points = np.stack([
+        xc + R * np.cos(arc_angles),
+        yc + R * np.sin(arc_angles)
+    ], axis=-1).astype(np.int32)
+
+    # 创建 mask 并画出拟合的弧线(扩展为像素块)
+    mask = np.zeros(shape, dtype=np.uint8)
+    for pt in arc_points:
+        cv2.circle(mask, tuple(pt), radius=radius, color=255, thickness=-1)
+
+    return mask, arc_points.tolist()
+
+
+def build_shapes_from_mask(mask, label="arc"):
+    contours, _ = cv2.findContours(mask, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
+    shapes = []
+
+    for contour in contours:
+        contour = contour.squeeze()
+        if len(contour.shape) != 2 or contour.shape[0] < 3:
+            continue  # 不是合法 polygon
+
+        points = contour.tolist()
+        xs = [p[0] for p in points]
+        ys = [p[1] for p in points]
+
+        shape = {
+            "label": label,
+            "points": points,
+            "shape_type": "polygon",
+            "flags": {},
+            "xmin": int(min(xs)),
+            "ymin": int(min(ys)),
+            "xmax": int(max(xs)),
+            "ymax": int(max(ys))
+        }
+        shapes.append(shape)
+
+    return shapes
+
+
+def process_folder_labelme(input_dir, output_dir, point_step_deg=0.5, radius=2):
+    os.makedirs(output_dir, exist_ok=True)
+
+    for file_name in tqdm(os.listdir(input_dir)):
+        if not file_name.endswith(".json"):
+            continue
+
+        json_path = os.path.join(input_dir, file_name)
+        image_path = json_path.replace(".json", ".jpg")
+        if not os.path.exists(image_path):
+            print(f"图像不存在,跳过:{image_path}")
+            continue
+
+        with open(json_path, "r") as f:
+            label_data = json.load(f)
+
+        points_list = [item['points'][0] for item in label_data['shapes'] if item['label'] == 'arc']
+        if len(points_list) != 3:
+            print(f"{file_name} 中 arc 点数不足 3,跳过")
+            continue
+
+        image = cv2.imread(image_path)
+        h, w = image.shape[:2]
+        points = np.array(points_list)
+        mask, arc_points = fit_arc_and_create_mask(points, (h, w), point_step_deg, radius)
+
+        shapes = build_shapes_from_mask(mask, label="arc")
+        if len(shapes) == 0:
+            print(f"{file_name} 拟合失败,跳过")
+            continue
+
+        output_json = {
+            "version": "5.0.1",
+            "flags": {},
+            "shapes": shapes,
+            "imagePath": os.path.basename(image_path),
+            "imageHeight": h,
+            "imageWidth": w
+        }
+
+        base_name = os.path.splitext(os.path.basename(image_path))[0]
+        out_json_path = os.path.join(output_dir, base_name + ".json")
+        out_img_path = os.path.join(output_dir, base_name + ".jpg")
+
+        with open(out_json_path, "w") as f:
+            json.dump(output_json, f, indent=4)
+
+        shutil.copy(image_path, out_img_path)
+
+    print(f"\n✅ 所有 arc 区域已保存为 labelme 格式(图像 + json)到目录:{output_dir}")
+
+
+# ===== 修改为你自己的输入输出路径 =====
+input_dir = r"G:\python_ws_g\data\arc"
+output_dir = r"G:\python_ws_g\data\arcout"
+point_step_deg = 0.2
+draw_radius = 3
+
+
+if __name__ == "__main__":
+    process_folder_labelme(input_dir, output_dir, point_step_deg, draw_radius)

+ 55 - 0
utils/data_process/zyh/rgbd/mask_choumi/show.py

@@ -0,0 +1,55 @@
+import os
+import json
+import cv2
+import numpy as np
+
+input_folder = r"G:\python_ws_g\data\arcout"   # 你的 JSON + 图片输入文件夹
+output_folder = r"G:\python_ws_g\data\arcshow"  # 保存带标注图片的文件夹
+
+MAX_WIDTH = 1280
+MAX_HEIGHT = 720
+
+def resize_to_fit(image, max_width=MAX_WIDTH, max_height=MAX_HEIGHT):
+    h, w = image.shape[:2]
+    scale = min(max_width / w, max_height / h, 1.0)  # 只缩小不放大
+    new_size = (int(w * scale), int(h * scale))
+    resized_image = cv2.resize(image, new_size, interpolation=cv2.INTER_AREA)
+    return resized_image
+
+def visualize_and_save(input_folder, output_folder):
+    os.makedirs(output_folder, exist_ok=True)
+
+    files = [f for f in os.listdir(input_folder) if f.endswith('.json')]
+    files.sort()
+
+    for json_file in files:
+        json_path = os.path.join(input_folder, json_file)
+        with open(json_path, 'r', encoding='utf-8') as f:
+            data = json.load(f)
+
+        image_path = os.path.join(input_folder, data["imagePath"])
+        if not os.path.exists(image_path):
+            print(f"图像不存在:{image_path}")
+            continue
+
+        image = cv2.imread(image_path)
+
+        for shape in data["shapes"]:
+            label = shape["label"]
+            points = shape["points"]
+            pts = np.array(points, dtype=np.int32).reshape((-1, 1, 2))
+
+            color = (0, 255, 0)
+            cv2.polylines(image, [pts], isClosed=True, color=color, thickness=2)
+            x0, y0 = pts[0][0]
+            cv2.putText(image, label, (x0, y0 - 5), cv2.FONT_HERSHEY_SIMPLEX, 0.6, color, 1)
+
+        resized_image = resize_to_fit(image)
+
+        base_name = os.path.splitext(json_file)[0]
+        save_path = os.path.join(output_folder, base_name + ".jpg")
+        cv2.imwrite(save_path, resized_image)
+        print(f"保存图片: {save_path}")
+
+if __name__ == "__main__":
+    visualize_and_save(input_folder, output_folder)

+ 156 - 0
utils/data_process/zyh/rgbd/mask_xishu/manycircle.py

@@ -0,0 +1,156 @@
+import os
+import json
+import shutil
+import numpy as np
+import cv2
+from scipy.optimize import leastsq
+from tqdm import tqdm
+
+
+def calc_R(x, y, xc, yc):
+    return np.sqrt((x - xc) ** 2 + (y - yc) ** 2)
+
+
+def f(c, x, y):
+    Ri = calc_R(x, y, *c)
+    return Ri - Ri.mean()
+
+def fit_arc_and_create_mask(points, shape, point_step_deg=0.5, radius=2):
+    x_data = points[:, 0]
+    y_data = points[:, 1]
+    center_estimate = np.mean(x_data), np.mean(y_data)
+    try:
+        center, _ = leastsq(f, center_estimate, args=(x_data, y_data))
+        xc, yc = center
+        Ri = calc_R(x_data, y_data, xc, yc)
+        R = Ri.mean()
+
+        h, w = shape
+        if R <= 0 or not np.isfinite(R) or not np.isfinite(xc) or not np.isfinite(yc):
+            raise ValueError("拟合圆非法")
+
+        # 生成完整圆的点
+        circle_angles = np.linspace(0, 2 * np.pi, int(360 / point_step_deg), endpoint=False)
+        full_circle_points = np.stack([
+            xc + R * np.cos(circle_angles),
+            yc + R * np.sin(circle_angles)
+        ], axis=-1).astype(np.float32)
+
+        # 只保留图像内部的点
+        in_bounds_mask = (
+            (full_circle_points[:, 0] >= 0) & (full_circle_points[:, 0] < w) &
+            (full_circle_points[:, 1] >= 0) & (full_circle_points[:, 1] < h)
+        )
+        clipped_points = full_circle_points[in_bounds_mask]
+
+        if len(clipped_points) < 3:
+            print(f"拟合失败使用的三个点为:\n{points}")
+            raise ValueError("拟合圆点全部在图像外")
+
+        return None, clipped_points.tolist()
+
+    except Exception as e:
+        print(f"⚠️ 圆拟合失败:{e}")
+        return None, []
+
+
+
+
+
+def build_shapes_from_points(points, label="arc"):
+    points_np = np.array(points)
+    if points_np.shape[0] < 3:
+        return []
+
+    xs = points_np[:, 0]
+    ys = points_np[:, 1]
+
+    shape = {
+        "label": label,
+        "points": points,
+        "shape_type": "polygon",
+        "flags": {},
+        "xmin": int(xs.min()),
+        "ymin": int(ys.min()),
+        "xmax": int(xs.max()),
+        "ymax": int(ys.max())
+    }
+    return [shape]
+
+
+def process_folder_labelme(input_dir, output_dir, point_step_deg=0.5, radius=2):
+    os.makedirs(output_dir, exist_ok=True)
+
+    for file_name in tqdm(os.listdir(input_dir)):
+        if not file_name.endswith(".json"):
+            continue
+
+        json_path = os.path.join(input_dir, file_name)
+        image_path = json_path.replace(".json", ".jpg")
+        if not os.path.exists(image_path):
+            print(f"图像不存在,跳过:{image_path}")
+            continue
+
+        with open(json_path, "r") as f:
+            label_data = json.load(f)
+
+        arc_points_raw = [item['points'][0] for item in label_data['shapes']
+                          if item.get('label') == 'arc' and len(item.get('points', [])) == 1]
+
+        if len(arc_points_raw) < 3:
+            print(f"{file_name} 中 arc 点数不足 3,跳过")
+            continue
+
+        image = cv2.imread(image_path)
+        h, w = image.shape[:2]
+
+        arc_shapes = []
+        num_groups = len(arc_points_raw) // 3
+
+        for i in range(num_groups):
+            group_points = arc_points_raw[i*3:(i+1)*3]
+            if len(group_points) < 3:
+                print(f"{file_name} 第 {i+1} 组点数不足 3,跳过")
+                continue
+            points = np.array(group_points)
+            try:
+                _, arc_pts = fit_arc_and_create_mask(points, (h, w), point_step_deg, radius)
+                shapes = build_shapes_from_points(arc_pts, label="arc")
+                arc_shapes.extend(shapes)
+            except Exception as e:
+                print(f"{file_name} 第 {i+1} 组拟合失败,跳过。错误:{e}")
+                continue
+
+        if len(arc_shapes) == 0:
+            print(f"{file_name} 没有成功拟合的 arc 区域,跳过")
+            continue
+
+        output_json = {
+            "version": "5.0.1",
+            "flags": {},
+            "shapes": arc_shapes,
+            "imagePath": os.path.basename(image_path),
+            "imageHeight": h,
+            "imageWidth": w
+        }
+
+        base_name = os.path.splitext(os.path.basename(image_path))[0]
+        out_json_path = os.path.join(output_dir, base_name + ".json")
+        out_img_path = os.path.join(output_dir, base_name + ".jpg")
+
+        with open(out_json_path, "w") as f:
+            json.dump(output_json, f, indent=4)
+
+        shutil.copy(image_path, out_img_path)
+
+    print(f"\n✅ 所有 arc 区域已保存为 labelme 格式(图像 + json)到目录:{output_dir}")
+
+
+# ===== 修改为你自己的输入输出路径 =====
+input_dir = r"G:\python_ws_g\data\test\jsonjpg_out"
+output_dir = r"G:\python_ws_g\data\test\jsonout"
+point_step_deg = 0.5
+draw_radius = 2
+
+if __name__ == "__main__":
+    process_folder_labelme(input_dir, output_dir, point_step_deg, draw_radius)

+ 127 - 0
utils/data_process/zyh/rgbd/mask_xishu/mask.py

@@ -0,0 +1,127 @@
+import os
+import json
+import shutil
+import numpy as np
+import cv2
+from scipy.optimize import leastsq
+from tqdm import tqdm
+
+
+def calc_R(x, y, xc, yc):
+    return np.sqrt((x - xc) ** 2 + (y - yc) ** 2)
+
+
+def f(c, x, y):
+    Ri = calc_R(x, y, *c)
+    return Ri - Ri.mean()
+
+
+def fit_arc_and_create_mask(points, shape, point_step_deg=0.5, radius=2):
+    x_data = points[:, 0]
+    y_data = points[:, 1]
+    center_estimate = np.mean(x_data), np.mean(y_data)
+    center, _ = leastsq(f, center_estimate, args=(x_data, y_data))
+    xc, yc = center
+    Ri = calc_R(x_data, y_data, xc, yc)
+    R = Ri.mean()
+
+    # 极角
+    angles = np.arctan2(points[:, 1] - yc, points[:, 0] - xc)
+    start_angle = np.min(angles)
+    end_angle = np.max(angles)
+
+    # 确保角度顺序正确
+    if end_angle - start_angle > np.pi:
+        start_angle, end_angle = end_angle, start_angle + 2 * np.pi
+
+    arc_angles = np.arange(start_angle, end_angle, np.deg2rad(point_step_deg))
+    arc_points = np.stack([
+        xc + R * np.cos(arc_angles),
+        yc + R * np.sin(arc_angles)
+    ], axis=-1).astype(np.int32)
+
+    # 不返回mask了,直接返回点列表
+    return None, arc_points.tolist()
+
+
+def build_shapes_from_points(points, label="arc"):
+    points_np = np.array(points)
+    if points_np.shape[0] < 3:
+        return []
+
+    xs = points_np[:, 0]
+    ys = points_np[:, 1]
+
+    shape = {
+        "label": label,
+        "points": points,
+        "shape_type": "polygon",
+        "flags": {},
+        "xmin": int(xs.min()),
+        "ymin": int(ys.min()),
+        "xmax": int(xs.max()),
+        "ymax": int(ys.max())
+    }
+    return [shape]
+
+
+def process_folder_labelme(input_dir, output_dir, point_step_deg=0.5, radius=2):
+    os.makedirs(output_dir, exist_ok=True)
+
+    for file_name in tqdm(os.listdir(input_dir)):
+        if not file_name.endswith(".json"):
+            continue
+
+        json_path = os.path.join(input_dir, file_name)
+        image_path = json_path.replace(".json", ".jpg")
+        if not os.path.exists(image_path):
+            print(f"图像不存在,跳过:{image_path}")
+            continue
+
+        with open(json_path, "r") as f:
+            label_data = json.load(f)
+
+        points_list = [item['points'][0] for item in label_data['shapes'] if item['label'] == 'arc']
+        if len(points_list) != 3:
+            print(f"{file_name} 中 arc 点数不足 3,跳过")
+            continue
+
+        image = cv2.imread(image_path)
+        h, w = image.shape[:2]
+        points = np.array(points_list)
+        _, arc_points = fit_arc_and_create_mask(points, (h, w), point_step_deg, radius)
+
+        shapes = build_shapes_from_points(arc_points, label="arc")
+        if len(shapes) == 0:
+            print(f"{file_name} 拟合失败,跳过")
+            continue
+
+        output_json = {
+            "version": "5.0.1",
+            "flags": {},
+            "shapes": shapes,
+            "imagePath": os.path.basename(image_path),
+            "imageHeight": h,
+            "imageWidth": w
+        }
+
+        base_name = os.path.splitext(os.path.basename(image_path))[0]
+        out_json_path = os.path.join(output_dir, base_name + ".json")
+        out_img_path = os.path.join(output_dir, base_name + ".jpg")
+
+        with open(out_json_path, "w") as f:
+            json.dump(output_json, f, indent=4)
+
+        shutil.copy(image_path, out_img_path)
+
+    print(f"\n✅ 所有 arc 区域已保存为 labelme 格式(图像 + json)到目录:{output_dir}")
+
+
+# ===== 修改为你自己的输入输出路径 =====
+input_dir = r"G:\python_ws_g\data\arc"
+output_dir = r"G:\python_ws_g\data\arcout"
+point_step_deg = 0.5
+draw_radius = 2
+
+if __name__ == "__main__":
+    process_folder_labelme(input_dir, output_dir, point_step_deg, draw_radius)

+ 58 - 0
utils/data_process/zyh/rgbd/mask_xishu/show.py

@@ -0,0 +1,58 @@
+import os
+import json
+import cv2
+import numpy as np
+
+input_folder = r"G:\python_ws_g\data\test\jsonout"   # 你的 JSON + 图片输入文件夹
+output_folder = r"G:\python_ws_g\data\test\arcshow"  # 保存带标注图片的文件夹
+# input_folder = r"G:\python_ws_g\data\xiao\out"   # 你的 JSON + 图片输入文件夹
+# output_folder = r"G:\python_ws_g\data\xiao\arcshow"  # 保存带标注图片的文件夹
+
+MAX_WIDTH = 1280
+MAX_HEIGHT = 720
+
+def resize_to_fit(image, max_width=MAX_WIDTH, max_height=MAX_HEIGHT):
+    h, w = image.shape[:2]
+    scale = min(max_width / w, max_height / h, 1.0)  # 只缩小不放大
+    new_size = (int(w * scale), int(h * scale))
+    resized_image = cv2.resize(image, new_size, interpolation=cv2.INTER_AREA)
+    return resized_image
+
+
+def visualize_and_save(input_folder, output_folder):
+    os.makedirs(output_folder, exist_ok=True)
+
+    files = [f for f in os.listdir(input_folder) if f.endswith('.json')]
+    files.sort()
+
+    for json_file in files:
+        json_path = os.path.join(input_folder, json_file)
+        with open(json_path, 'r', encoding='utf-8') as f:
+            data = json.load(f)
+
+        image_path = os.path.join(input_folder, data["imagePath"])
+        if not os.path.exists(image_path):
+            print(f"图像不存在:{image_path}")
+            continue
+
+        image = cv2.imread(image_path)
+
+        for shape in data["shapes"]:
+            label = shape["label"]
+            points = shape["points"]
+            for pt in points:
+                x, y = int(pt[0]), int(pt[1])
+                cv2.circle(image, (x, y), radius=3, color=(0, 255, 0), thickness=-1)
+
+            x0, y0 = int(points[0][0]), int(points[0][1])
+            cv2.putText(image, label, (x0, y0 - 5), cv2.FONT_HERSHEY_SIMPLEX, 0.6, (0, 255, 0), 1)
+
+        resized_image = resize_to_fit(image)
+
+        base_name = os.path.splitext(json_file)[0]
+        save_path = os.path.join(output_folder, base_name + ".jpg")
+        cv2.imwrite(save_path, resized_image)
+        print(f"保存图片: {save_path}")
+
+if __name__ == "__main__":
+    visualize_and_save(input_folder, output_folder)

+ 81 - 0
utils/data_process/zyh/rgbd/pcd2rgbd.py

@@ -0,0 +1,81 @@
+import os
+import cv2
+import numpy as np
+import open3d as o3d
+from glob import glob
+from tifffile import imwrite as tif_write
+
+# 相机内参矩阵
+K = np.array([
+    [1.30449e3, 0,         5.2602e2],
+    [0,         1.30449e3, 1.07432e3],
+    [0,         0,         1]
+])
+fx, fy = K[0, 0], K[1, 1]
+cx, cy = K[0, 2], K[1, 2]
+
+# 图像尺寸
+height, width = 2000, 2000
+
+def pointscloud2colorimg(points):
+    color_image = np.zeros((height, width, 3), dtype=np.float32)
+    for point in points:
+        X, Y, Z, r, g, b = point
+        if Z > 0:
+            u = int((X * fx) / Z + cx)
+            v = int((Y * fy) / Z + cy)
+            if 0 <= u < width and 0 <= v < height:
+                color_image[v, u] = [r * 255, g * 255, b * 255]
+    return color_image
+
+def pointscloud2depthmap(points):
+    depth_map = np.zeros((height, width), dtype=np.float32)
+    for point in points:
+        X, Y, Z, *_ = point
+        if Z > 0:
+            u = int((X * fx) / Z + cx)
+            v = int((Y * fy) / Z + cy)
+            if 0 <= u < width and 0 <= v < height:
+                depth_map[v, u] = Z
+    return depth_map
+
+def process_pcd_file(pcd_path, rgbd_dir):
+    filename = os.path.splitext(os.path.basename(pcd_path))[0]
+    print(f"Processing: {filename}")
+
+    pcd = o3d.io.read_point_cloud(pcd_path)
+    points = np.asarray(pcd.points)
+    colors = np.asarray(pcd.colors)
+
+    if points.shape[0] == 0 or colors.shape[0] == 0:
+        print(f"Skipping empty or invalid PCD: {filename}")
+        return
+
+    points_colored = np.hstack((points, colors))
+
+    color_img = pointscloud2colorimg(points_colored)
+    depth_map = pointscloud2depthmap(points_colored)
+
+    # RGB 转 uint8,Depth 保持 float32
+    color_img_u8 = np.clip(color_img, 0, 255).astype(np.uint8)
+    depth_map_mm = depth_map.astype(np.float32)
+
+    # 合并为 4 通道 RGBD 图像(float32)
+    rgbd = np.concatenate((color_img_u8, depth_map_mm[..., np.newaxis]), axis=2)
+    tif_write(os.path.join(rgbd_dir, f"{filename}_rgbd.tiff"), rgbd, photometric='rgb')
+
+    print(f"Saved RGBD TIFF for {filename}")
+
+input_dir = r"G:\python_ws_g\data\pcd"
+output_base = os.path.dirname(input_dir)
+
+rgbd_dir = os.path.join(output_base, "rgbd_tiff")
+os.makedirs(rgbd_dir, exist_ok=True)
+
+# 获取 PCD 文件
+pcd_files = glob(os.path.join(input_dir, "*.pcd"))
+print(f"Found {len(pcd_files)} PCD files.")
+
+# 批量处理
+for pcd_path in pcd_files:
+    process_pcd_file(pcd_path, rgbd_dir)

+ 15 - 0
utils/data_process/zyh/rgbd/test/1delstrongjson.py

@@ -0,0 +1,15 @@
+import os
+
+# 设置目标目录路径
+target_dir = r'G:\python_ws_g\data\test\json_jpg'  # 修改为你的目标文件夹路径
+
+# 遍历文件夹中的所有文件
+for filename in os.listdir(target_dir):
+    if not (filename.endswith('_color.json') or filename.endswith('_color.jpg')):
+        # 如果不是 _color.json 或 _color.jpg,则删除
+        file_path = os.path.join(target_dir, filename)
+        try:
+            os.remove(file_path)
+            print(f"已删除: {file_path}")
+        except Exception as e:
+            print(f"删除失败: {file_path}, 原因: {e}")

+ 96 - 0
utils/data_process/zyh/rgbd/test/3weizhuang.py

@@ -0,0 +1,96 @@
+import os
+import json
+import shutil
+import numpy as np
+from tqdm import tqdm
+
+
+def random_12_points(h, w, num_points=12, margin=20):
+    xs = np.random.uniform(margin, w - margin, num_points)
+    ys = np.random.uniform(margin, h - margin, num_points)
+    points = np.stack([xs, ys], axis=-1).astype(np.float32)
+    return points.tolist()
+
+
+def build_point_shape(pt, label="arc"):
+    return {
+        "label": label,
+        "points": [pt],
+        "shape_type": "point",
+        "flags": {},
+        "attributes": {},
+        "group_id": None,
+        "description": "",
+        "difficult": False,
+        "kie_linking": []
+    }
+
+
+def process_json_folder(input_dir, output_dir, num_points=12, margin=20):
+    os.makedirs(output_dir, exist_ok=True)
+
+    for file_name in tqdm(os.listdir(input_dir)):
+        if not file_name.endswith(".json"):
+            continue
+
+        json_path = os.path.join(input_dir, file_name)
+        with open(json_path, "r", encoding="utf-8") as f:
+            data = json.load(f)
+
+        arc_points_raw = [item['points'][0] for item in data['shapes']
+                          if item.get('label') == 'arc' and item.get('shape_type') == 'point']
+
+        if len(arc_points_raw) < 3:
+            print(f"{file_name} 中 arc 点数不足 3,跳过")
+            continue
+
+        num_groups = len(arc_points_raw) // 3
+        new_shapes = []
+        h = data.get("imageHeight", 2000)
+        w = data.get("imageWidth", 2000)
+
+        for i in range(num_groups):
+            # 这里不使用拟合,直接随机生成12个点
+            try:
+                circle_pts = random_12_points(h, w, num_points=num_points, margin=margin)
+                for pt in circle_pts:
+                    shape = build_point_shape(pt, label="arc")
+                    new_shapes.append(shape)
+
+            except Exception as e:
+                print(f"{file_name} 第 {i+1} 组生成失败,跳过。错误:{e}")
+                continue
+
+        if not new_shapes:
+            continue
+
+        new_json = {
+            "version": data.get("version", "5.0.1"),
+            "flags": {},
+            "shapes": new_shapes,
+            "imagePath": data["imagePath"],
+            "imageHeight": h,
+            "imageWidth": w,
+            "imageData": None
+        }
+
+        base_name = os.path.splitext(file_name)[0]
+        out_json_path = os.path.join(output_dir, base_name + ".json")
+        out_img_path = os.path.join(output_dir, data["imagePath"])
+
+        with open(out_json_path, "w", encoding="utf-8") as f:
+            json.dump(new_json, f, indent=4)
+
+        old_img_path = os.path.join(input_dir, data["imagePath"])
+        if os.path.exists(old_img_path):
+            shutil.copy(old_img_path, out_img_path)
+
+    print(f"\n✅ 已处理完成,输出保存至:{output_dir}")
+
+
+# ========= 路径设置 =========
+input_dir = r"G:\python_ws_g\data\test\jsonjpg"
+output_dir = r"G:\python_ws_g\data\test\jsonjpg_weizhuang12"
+
+if __name__ == "__main__":
+    process_json_folder(input_dir, output_dir, num_points=12, margin=20)

+ 156 - 0
utils/data_process/zyh/rgbd/test/4manycircle.py

@@ -0,0 +1,156 @@
+import os
+import json
+import shutil
+import numpy as np
+import cv2
+from scipy.optimize import leastsq
+from tqdm import tqdm
+
+
+def calc_R(x, y, xc, yc):
+    return np.sqrt((x - xc) ** 2 + (y - yc) ** 2)
+
+
+def f(c, x, y):
+    Ri = calc_R(x, y, *c)
+    return Ri - Ri.mean()
+
+def fit_arc_and_create_mask(points, shape, point_step_deg=0.5, radius=2):
+    x_data = points[:, 0]
+    y_data = points[:, 1]
+    center_estimate = np.mean(x_data), np.mean(y_data)
+    try:
+        center, _ = leastsq(f, center_estimate, args=(x_data, y_data))
+        xc, yc = center
+        Ri = calc_R(x_data, y_data, xc, yc)
+        R = Ri.mean()
+
+        h, w = shape
+        if R <= 0 or not np.isfinite(R) or not np.isfinite(xc) or not np.isfinite(yc):
+            raise ValueError("拟合圆非法")
+
+        # 生成完整圆的点
+        circle_angles = np.linspace(0, 2 * np.pi, int(360 / point_step_deg), endpoint=False)
+        full_circle_points = np.stack([
+            xc + R * np.cos(circle_angles),
+            yc + R * np.sin(circle_angles)
+        ], axis=-1).astype(np.float32)
+
+        # 只保留图像内部的点
+        in_bounds_mask = (
+            (full_circle_points[:, 0] >= 0) & (full_circle_points[:, 0] < w) &
+            (full_circle_points[:, 1] >= 0) & (full_circle_points[:, 1] < h)
+        )
+        clipped_points = full_circle_points[in_bounds_mask]
+
+        if len(clipped_points) < 3:
+            print(f"拟合失败使用的三个点为:\n{points}")
+            raise ValueError("拟合圆点全部在图像外")
+
+        return None, clipped_points.tolist()
+
+    except Exception as e:
+        print(f"⚠️ 圆拟合失败:{e}")
+        return None, []
+
+
+
+
+
+def build_shapes_from_points(points, label="arc"):
+    points_np = np.array(points)
+    if points_np.shape[0] < 3:
+        return []
+
+    xs = points_np[:, 0]
+    ys = points_np[:, 1]
+
+    shape = {
+        "label": label,
+        "points": points,
+        "shape_type": "polygon",
+        "flags": {},
+        "xmin": int(xs.min()),
+        "ymin": int(ys.min()),
+        "xmax": int(xs.max()),
+        "ymax": int(ys.max())
+    }
+    return [shape]
+
+
+def process_folder_labelme(input_dir, output_dir, point_step_deg=0.5, radius=2):
+    os.makedirs(output_dir, exist_ok=True)
+
+    for file_name in tqdm(os.listdir(input_dir)):
+        if not file_name.endswith(".json"):
+            continue
+
+        json_path = os.path.join(input_dir, file_name)
+        image_path = json_path.replace(".json", ".jpg")
+        if not os.path.exists(image_path):
+            print(f"图像不存在,跳过:{image_path}")
+            continue
+
+        with open(json_path, "r") as f:
+            label_data = json.load(f)
+
+        arc_points_raw = [item['points'][0] for item in label_data['shapes']
+                          if item.get('label') == 'arc' and len(item.get('points', [])) == 1]
+
+        if len(arc_points_raw) < 3:
+            print(f"{file_name} 中 arc 点数不足 3,跳过")
+            continue
+
+        image = cv2.imread(image_path)
+        h, w = image.shape[:2]
+
+        arc_shapes = []
+        num_groups = len(arc_points_raw) // 3
+
+        for i in range(num_groups):
+            group_points = arc_points_raw[i*3:(i+1)*3]
+            if len(group_points) < 3:
+                print(f"{file_name} 第 {i+1} 组点数不足 3,跳过")
+                continue
+            points = np.array(group_points)
+            try:
+                _, arc_pts = fit_arc_and_create_mask(points, (h, w), point_step_deg, radius)
+                shapes = build_shapes_from_points(arc_pts, label="arc")
+                arc_shapes.extend(shapes)
+            except Exception as e:
+                print(f"{file_name} 第 {i+1} 组拟合失败,跳过。错误:{e}")
+                continue
+
+        if len(arc_shapes) == 0:
+            print(f"{file_name} 没有成功拟合的 arc 区域,跳过")
+            continue
+
+        output_json = {
+            "version": "5.0.1",
+            "flags": {},
+            "shapes": arc_shapes,
+            "imagePath": os.path.basename(image_path),
+            "imageHeight": h,
+            "imageWidth": w
+        }
+
+        base_name = os.path.splitext(os.path.basename(image_path))[0]
+        out_json_path = os.path.join(output_dir, base_name + ".json")
+        out_img_path = os.path.join(output_dir, base_name + ".jpg")
+
+        with open(out_json_path, "w") as f:
+            json.dump(output_json, f, indent=4)
+
+        shutil.copy(image_path, out_img_path)
+
+    print(f"\n✅ 所有 arc 区域已保存为 labelme 格式(图像 + json)到目录:{output_dir}")
+
+
+# ===== 修改为你自己的输入输出路径 =====
+input_dir = r"G:\python_ws_g\data\test\jsonjpg_weizhuang12"
+output_dir = r"G:\python_ws_g\data\test\circle"
+point_step_deg = 0.5
+draw_radius = 2
+
+if __name__ == "__main__":
+    process_folder_labelme(input_dir, output_dir, point_step_deg, draw_radius)

+ 58 - 0
utils/data_process/zyh/rgbd/test/5show.py

@@ -0,0 +1,58 @@
+import os
+import json
+import cv2
+import numpy as np
+
+input_folder = r"G:\python_ws_g\data\test\jsonout"   # 你的 JSON + 图片输入文件夹
+output_folder = r"G:\python_ws_g\data\test\arcshow"  # 保存带标注图片的文件夹
+# input_folder = r"G:\python_ws_g\data\xiao\out"   # 你的 JSON + 图片输入文件夹
+# output_folder = r"G:\python_ws_g\data\xiao\arcshow"  # 保存带标注图片的文件夹
+
+MAX_WIDTH = 1280
+MAX_HEIGHT = 720
+
+def resize_to_fit(image, max_width=MAX_WIDTH, max_height=MAX_HEIGHT):
+    h, w = image.shape[:2]
+    scale = min(max_width / w, max_height / h, 1.0)  # 只缩小不放大
+    new_size = (int(w * scale), int(h * scale))
+    resized_image = cv2.resize(image, new_size, interpolation=cv2.INTER_AREA)
+    return resized_image
+
+
+def visualize_and_save(input_folder, output_folder):
+    os.makedirs(output_folder, exist_ok=True)
+
+    files = [f for f in os.listdir(input_folder) if f.endswith('.json')]
+    files.sort()
+
+    for json_file in files:
+        json_path = os.path.join(input_folder, json_file)
+        with open(json_path, 'r', encoding='utf-8') as f:
+            data = json.load(f)
+
+        image_path = os.path.join(input_folder, data["imagePath"])
+        if not os.path.exists(image_path):
+            print(f"图像不存在:{image_path}")
+            continue
+
+        image = cv2.imread(image_path)
+
+        for shape in data["shapes"]:
+            label = shape["label"]
+            points = shape["points"]
+            for pt in points:
+                x, y = int(pt[0]), int(pt[1])
+                cv2.circle(image, (x, y), radius=3, color=(0, 255, 0), thickness=-1)
+
+            x0, y0 = int(points[0][0]), int(points[0][1])
+            cv2.putText(image, label, (x0, y0 - 5), cv2.FONT_HERSHEY_SIMPLEX, 0.6, (0, 255, 0), 1)
+
+        resized_image = resize_to_fit(image)
+
+        base_name = os.path.splitext(json_file)[0]
+        save_path = os.path.join(output_folder, base_name + ".jpg")
+        cv2.imwrite(save_path, resized_image)
+        print(f"保存图片: {save_path}")
+
+if __name__ == "__main__":
+    visualize_and_save(input_folder, output_folder)

+ 35 - 0
utils/data_process/zyh/rgbd/test/6matchtiffjson.py

@@ -0,0 +1,35 @@
+import os
+import shutil
+
+# 输入文件夹路径:只需修改这三行
+json_folder = r"G:\python_ws_g\data\test\jsonjpg"
+tiff_folder = r"G:\python_ws_g\data\test\tiff"
+output_folder = r"G:\python_ws_g\data\test\tiff_json"
+
+# 创建输出文件夹(如不存在)
+os.makedirs(output_folder, exist_ok=True)
+
+# 提取 json 文件中的标识前缀(用于匹配)
+json_files = [f for f in os.listdir(json_folder) if f.endswith('_color.json')]
+json_keys = {f.replace('_color.json', ''): f for f in json_files}
+
+# 遍历 tiff 文件,尝试匹配 json
+matched_count = 0
+for tiff_name in os.listdir(tiff_folder):
+    if not tiff_name.endswith('.tiff'):
+        continue
+    if '_rgbd.tiff' not in tiff_name:
+        continue
+
+    key = tiff_name.replace('_rgbd.tiff', '')
+    if key in json_keys:
+        # 匹配成功,复制两份文件到目标目录
+        json_src = os.path.join(json_folder, json_keys[key])
+        tiff_src = os.path.join(tiff_folder, tiff_name)
+
+        shutil.copy(json_src, os.path.join(output_folder, json_keys[key]))
+        shutil.copy(tiff_src, os.path.join(output_folder, tiff_name))
+        print(f"匹配并复制:{json_keys[key]} <-> {tiff_name}")
+        matched_count += 1
+
+print(f"\n✅ 共复制 {matched_count} 对文件到:{output_folder}")

+ 16 - 0
utils/data_process/zyh/rgbd/test/7rename.py

@@ -0,0 +1,16 @@
+import os
+
+# 设置你的目标文件夹路径
+folder_path = r'G:\python_ws_g\data\test\tiff_json'  # 例如 r'G:\data\LaserData'
+
+# 遍历该文件夹下的所有文件
+for filename in os.listdir(folder_path):
+    if filename.endswith('_color.json'):
+        # 构造旧文件路径和新文件路径
+        old_path = os.path.join(folder_path, filename)
+        new_filename = filename.replace('_color.json', '_rgbd.json')
+        new_path = os.path.join(folder_path, new_filename)
+
+        # 重命名文件
+        os.rename(old_path, new_path)
+        print(f'Renamed: {filename} -> {new_filename}')

+ 102 - 0
utils/data_process/zyh/rgbd/test/8data_spliter.py

@@ -0,0 +1,102 @@
+"""
+该脚本将源目录下同名的 .json 和 .diff 文件配对,
+并按如下结构将其整理至目标目录:
+
+输出结构如下:
+
+output_dir/
+├── images/
+│   ├── train/
+│   └── val/
+└── labels/
+    ├── train/
+    └── val/
+
+其中:
+- images/train 和 val 存放的是 `.diff` 文件
+- labels/train 和 val 存放的是 `.json` 文件
+"""
+
+import os
+import shutil
+import random
+from pathlib import Path
+
+def organize_data(
+    src_dir,
+    dest_dir,
+    image_extensions=['.tiff'],
+    label_extensions=['.json'],
+    val_ratio=0.2
+):
+    src_dir = Path(src_dir)
+    dest_dir = Path(dest_dir)
+
+    image_dir = dest_dir / 'images'
+    label_dir = dest_dir / 'labels'
+
+    # 创建文件夹结构
+    for split in ['train', 'val']:
+        (image_dir / split).mkdir(parents=True, exist_ok=True)
+        (label_dir / split).mkdir(parents=True, exist_ok=True)
+
+    # 获取所有文件
+    files = list(src_dir.glob('*'))
+    name_to_files = {}
+
+    # 分组:同名文件归为一组
+    for f in files:
+        stem = f.stem
+        name_to_files.setdefault(stem, []).append(f)
+
+    # 筛选出同时包含 label 和 image 的样本
+    paired_samples = []
+    for name, file_group in name_to_files.items():
+        image_file = next((f for f in file_group if f.suffix in image_extensions), None)
+        label_file = next((f for f in file_group if f.suffix in label_extensions), None)
+        if image_file and label_file:
+            paired_samples.append((image_file, label_file))
+        else:
+            print(f"⚠️ Skipping unpaired files for: {name}")
+
+    # 打乱并划分
+    random.shuffle(paired_samples)
+    split_idx = int(len(paired_samples) * (1 - val_ratio))
+    train_samples = paired_samples[:split_idx]
+    val_samples = paired_samples[split_idx:]
+
+    # 拷贝函数
+    def copy_samples(samples, split):
+        for img, lbl in samples:
+            shutil.copy(img, image_dir / split / img.name)
+            shutil.copy(lbl, label_dir / split / lbl.name)
+
+    # 执行拷贝
+    copy_samples(train_samples, 'train')
+    copy_samples(val_samples, 'val')
+
+    print(f"\n✅ Done! Processed {len(paired_samples)} pairs.")
+    print(f"Train: {len(train_samples)}, Val: {len(val_samples)}")
+
+if __name__ == "__main__":
+    # 输入输出目录(可修改)
+    source_dir = r"G:\python_ws_g\data\test\tiff_json"
+
+    parent_dir = os.path.dirname(source_dir)
+    output_dir = os.path.join(parent_dir, "a_dataset")
+
+    # 后缀名列表,方便以后扩展其他格式
+    image_exts = ['.tiff','.jpg']
+    label_exts = ['.json']
+
+    organize_data(source_dir, output_dir, image_exts, label_exts)
+
+
+
+
+
+
+
+
+
+

+ 28 - 0
utils/data_process/zyh/rgbd/tiffshow.py

@@ -0,0 +1,28 @@
+import tifffile as tiff
+import matplotlib.pyplot as plt
+import numpy as np
+
+# 读取 TIFF 图像
+img = tiff.imread(r"\\192.168.50.222\share2\zyh\data\rgbd_line\rgbd_tiff\2025-07-21-09-10-52_LaserData_ID019504_rgbd.tiff")  # 替换成你的路径
+
+# 检查图像维度
+print("Image shape:", img.shape)
+
+# 假设图像是 H x W x C 结构(通道在最后)
+if img.ndim == 3 and img.shape[2] >= 3:
+    rgb_img = img[:, :, :3]  # 取前三个通道
+elif img.ndim == 3 and img.shape[0] >= 3:
+    rgb_img = np.transpose(img[:3, :, :], (1, 2, 0))  # 如果是 C x H x W 格式
+else:
+    raise ValueError("图像不是 RGB 结构,或通道数不足3个")
+
+# 归一化处理以便可视化
+rgb_img = rgb_img.astype(np.float32)
+if rgb_img.max() > 1.0:
+    rgb_img /= rgb_img.max()
+
+# 显示图像
+plt.imshow(rgb_img)
+plt.title("First 3 Channels of TIFF")
+plt.axis('off')
+plt.show()

BIN
utils/data_process/zyh/simplify_process_and_enhance_data/._.DS_Store


+ 0 - 0
utils/data_process/zyh/simplify_process_and_enhance_data/__init__.py


+ 321 - 0
utils/data_process/zyh/simplify_process_and_enhance_data/a_data_augmentation.py

@@ -0,0 +1,321 @@
+import os
+import json
+import cv2
+import numpy as np
+import random
+import copy
+import shutil
+from typing import List, Tuple, Dict, Any
+
+# 数据增强配置
+config = {
+    'rotate': True,
+    'crop': True,
+    'occlude': False,
+    'flip': True,
+    'brightness': True,
+    'blur': True,
+    'noise': True,
+    'scale': True,
+    'color_jitter': True,
+    'adjust_brightness_contrast': True,
+    'shadow': True,
+    'highlight': True
+}
+
+# --------------------------- 增强模块 ---------------------------
+
+class Compose:
+    def __init__(self, transforms: list):
+        self.transforms = transforms
+
+    def __call__(self, image: np.ndarray, label_data: Dict) -> Tuple[np.ndarray, Dict]:
+        for t in self.transforms:
+            image, label_data = t(image, label_data)
+        return image, label_data
+
+
+class RandomFlip:
+    def __init__(self, probability: float = 0.5):
+        self.probability = probability
+
+    def __call__(self, image: np.ndarray, label_data: Dict) -> Tuple[np.ndarray, Dict]:
+        if random.random() > self.probability:
+            return image, label_data
+
+        flip_code = random.choice([-1, 0, 1])
+        flipped_image = cv2.flip(image, flip_code)
+        h, w = image.shape[:2]
+
+        new_shapes = []
+        for shape in label_data['shapes']:
+            new_shape = copy.deepcopy(shape)
+            new_points = []
+            for x, y in shape['points']:
+                new_x = w - x if flip_code in [-1, 1] else x
+                new_y = h - y if flip_code in [-1, 0] else y
+                new_points.append([new_x, new_y])
+            new_shape['points'] = new_points
+            new_shapes.append(new_shape)
+
+        label_data['shapes'] = new_shapes
+        return flipped_image, label_data
+
+
+class RandomBrightness:
+    def __init__(self, probability: float = 0.5, delta: int = 30):
+        self.probability = probability
+        self.delta = delta
+
+    def __call__(self, image: np.ndarray, label_data: Dict) -> Tuple[np.ndarray, Dict]:
+        if random.random() > self.probability:
+            return image, label_data
+        value = random.randint(-self.delta, self.delta)
+        hsv = cv2.cvtColor(image, cv2.COLOR_BGR2HSV)
+        hsv[:, :, 2] = np.clip(hsv[:, :, 2] + value, 0, 255)
+        return cv2.cvtColor(hsv, cv2.COLOR_HSV2BGR), label_data
+
+
+class RandomBlur:
+    def __init__(self, probability: float = 0.5, ksize: Tuple[int, int] = (5, 5)):
+        self.probability = probability
+        self.ksize = ksize
+
+    def __call__(self, image: np.ndarray, label_data: Dict) -> Tuple[np.ndarray, Dict]:
+        if random.random() > self.probability:
+            return image, label_data
+        return cv2.GaussianBlur(image, self.ksize, 0), label_data
+
+
+class RandomNoise:
+    def __init__(self, probability: float = 0.5, noise_level: int = 25):
+        self.probability = probability
+        self.noise_level = noise_level
+
+    def __call__(self, image: np.ndarray, label_data: Dict) -> Tuple[np.ndarray, Dict]:
+        if random.random() > self.probability:
+            return image, label_data
+        noise = np.random.randint(-self.noise_level, self.noise_level, image.shape, dtype=np.int16)
+        noisy_image = np.clip(image.astype(np.int16) + noise, 0, 255).astype(np.uint8)
+        return noisy_image, label_data
+
+
+class RandomScale:
+    def __init__(self, probability: float = 0.5, scale_range: Tuple[float, float] = (0.8, 1.2)):
+        self.probability = probability
+        self.scale_range = scale_range
+
+    def __call__(self, image: np.ndarray, label_data: Dict) -> Tuple[np.ndarray, Dict]:
+        if random.random() > self.probability:
+            return image, label_data
+
+        h, w = image.shape[:2]
+        scale = random.uniform(*self.scale_range)
+        new_w, new_h = int(w * scale), int(h * scale)
+        scaled_image = cv2.resize(image, (new_w, new_h))
+
+        new_shapes = []
+        for shape in label_data['shapes']:
+            new_shape = copy.deepcopy(shape)
+            new_points = [[x * scale, y * scale] for x, y in shape['points']]
+            new_shape['points'] = new_points
+            new_shapes.append(new_shape)
+
+        label_data['shapes'] = new_shapes
+        label_data['imageHeight'] = new_h
+        label_data['imageWidth'] = new_w
+        return scaled_image, label_data
+
+
+class RandomColorJitter:
+    def __init__(self, probability: float = 0.5, alpha: float = 0.3):
+        self.probability = probability
+        self.alpha = alpha
+
+    def __call__(self, image: np.ndarray, label_data: Dict) -> Tuple[np.ndarray, Dict]:
+        if random.random() > self.probability:
+            return image, label_data
+        factors = np.random.uniform(1 - self.alpha, 1 + self.alpha, 3)
+        img = image.astype(np.float32)
+        img = np.clip(img * factors, 0, 255).astype(np.uint8)
+        return img, label_data
+
+
+class RandomRotate:
+    def __init__(self, probability: float = 0.7):
+        self.probability = probability
+
+    def __call__(self, image: np.ndarray, label_data: Dict) -> Tuple[np.ndarray, Dict]:
+        if random.random() > self.probability:
+            return image, label_data
+
+        h, w = image.shape[:2]
+        angle = random.uniform(-30, 30)
+        M = cv2.getRotationMatrix2D((w / 2, h / 2), angle, 1.0)
+        rotated = cv2.warpAffine(image, M, (w, h), flags=cv2.INTER_LINEAR, borderMode=cv2.BORDER_CONSTANT)
+
+        new_shapes = []
+        for shape in label_data['shapes']:
+            new_shape = copy.deepcopy(shape)
+            new_points = []
+            for x, y in shape['points']:
+                x_new = M[0][0] * x + M[0][1] * y + M[0][2]
+                y_new = M[1][0] * x + M[1][1] * y + M[1][2]
+                new_points.append([x_new, y_new])
+            new_shape['points'] = new_points
+            new_shapes.append(new_shape)
+
+        label_data['shapes'] = new_shapes
+        return rotated, label_data
+
+
+class RandomCrop:
+    def __init__(self, probability: float = 0.7):
+        self.probability = probability
+
+    def __call__(self, image: np.ndarray, label_data: Dict) -> Tuple[np.ndarray, Dict]:
+        if random.random() > self.probability:
+            return image, label_data
+
+        h, w = image.shape[:2]
+        crop_w = random.randint(int(w * 0.6), w)
+        crop_h = random.randint(int(h * 0.6), h)
+        x = random.randint(0, w - crop_w)
+        y = random.randint(0, h - crop_h)
+        cropped = image[y:y + crop_h, x:x + crop_w]
+
+        new_shapes = []
+        for shape in label_data['shapes']:
+            new_shape = copy.deepcopy(shape)
+            new_points = [[max(0, px - x), max(0, py - y)] for px, py in shape['points']]
+            new_shape['points'] = new_points
+            new_shapes.append(new_shape)
+
+        label_data['shapes'] = new_shapes
+        label_data['imageHeight'] = crop_h
+        label_data['imageWidth'] = crop_w
+        return cropped, label_data
+
+
+class RandomOcclusion:
+    def __init__(self, probability: float = 0.7):
+        self.probability = probability
+
+    def __call__(self, image: np.ndarray, label_data: Dict) -> Tuple[np.ndarray, Dict]:
+        if random.random() > self.probability:
+            return image, label_data
+
+        h, w = image.shape[:2]
+        for _ in range(random.randint(1, 3)):
+            ow = random.randint(80, 150)
+            oh = random.randint(80, 150)
+            x = random.randint(0, w - ow)
+            y = random.randint(0, h - oh)
+            cv2.rectangle(image, (x, y), (x + ow, y + oh), (0, 0, 0), -1)
+        return image, label_data
+
+
+class AdjustBrightnessContrast:
+    def __init__(self, brightness=0, contrast=1.0):
+        self.brightness = brightness
+        self.contrast = contrast
+
+    def __call__(self, image: np.ndarray, label_data: Dict) -> Tuple[np.ndarray, Dict]:
+        return cv2.convertScaleAbs(image, alpha=self.contrast, beta=self.brightness), label_data
+
+
+class SimulateShadow:
+    def __init__(self, shadow_intensity=0.5):
+        self.shadow_intensity = shadow_intensity
+
+    def __call__(self, image: np.ndarray, label_data: Dict) -> Tuple[np.ndarray, Dict]:
+        h = image.shape[0]
+        shadow = np.zeros_like(image)
+        for i in range(h):
+            shadow[i] = int(255 * self.shadow_intensity * (1 - i / h))
+        return cv2.addWeighted(image, 1, shadow, 0.5, 0), label_data
+
+
+class SimulateHighlight:
+    def __init__(self, highlight_intensity=0.5):
+        self.highlight_intensity = highlight_intensity
+
+    def __call__(self, image: np.ndarray, label_data: Dict) -> Tuple[np.ndarray, Dict]:
+        h = image.shape[0]
+        highlight = np.zeros_like(image)
+        for i in range(h):
+            highlight[i] = int(255 * self.highlight_intensity * (i / h))
+        return cv2.addWeighted(image, 1, highlight, 0.5, 0), label_data
+
+
+# ------------------------ 工具函数 ------------------------
+
+def build_transforms(config: Dict) -> List[Tuple[str, Any]]:
+    transforms = []
+    if config.get('rotate'): transforms.append(('rotate', RandomRotate()))
+    if config.get('crop'): transforms.append(('crop', RandomCrop()))
+    if config.get('occlude'): transforms.append(('occlude', RandomOcclusion()))
+    if config.get('flip'): transforms.append(('flip', RandomFlip()))
+    if config.get('brightness'): transforms.append(('brightness', RandomBrightness()))
+    if config.get('blur'): transforms.append(('blur', RandomBlur()))
+    if config.get('noise'): transforms.append(('noise', RandomNoise()))
+    if config.get('scale'): transforms.append(('scale', RandomScale()))
+    if config.get('color_jitter'): transforms.append(('color_jitter', RandomColorJitter()))
+    if config.get('adjust_brightness_contrast'):
+        transforms.append(('adjust_brightness_contrast', AdjustBrightnessContrast(brightness=30, contrast=1.5)))
+    if config.get('shadow'): transforms.append(('shadow', SimulateShadow(shadow_intensity=0.3)))
+    if config.get('highlight'): transforms.append(('highlight', SimulateHighlight(highlight_intensity=0.3)))
+    random.shuffle(transforms)
+    return transforms
+
+
+def process_file_separately(image_path: str, label_path: str, output_dir: str, transforms: List[Tuple[str, Any]]):
+    image = cv2.imread(image_path)
+    if image is None:
+        print(f"无法读取图像: {image_path}")
+        return
+
+    with open(label_path, 'r') as f:
+        label_data = json.load(f)
+
+    base_name = os.path.splitext(os.path.basename(image_path))[0]
+    for transform_name, transform in transforms:
+        transformed_image, transformed_label = transform(image.copy(), copy.deepcopy(label_data))
+        transformed_label['imageHeight'] = transformed_image.shape[0]
+        transformed_label['imageWidth'] = transformed_image.shape[1]
+
+        output_img = os.path.join(output_dir, f"{base_name}_{transform_name}.jpg")
+        output_json = os.path.join(output_dir, f"{base_name}_{transform_name}.json")
+        cv2.imwrite(output_img, transformed_image)
+        with open(output_json, 'w') as f:
+            json.dump(transformed_label, f, indent=2)
+
+
+def process_dataset(input_dir: str, output_dir: str, config: Dict):
+    os.makedirs(output_dir, exist_ok=True)
+    transforms = build_transforms(config)
+    count = 0
+
+    for fname in os.listdir(input_dir):
+        if fname.lower().endswith(('.jpg', '.jpeg', '.png')):
+            image_path = os.path.join(input_dir, fname)
+            label_path = os.path.join(input_dir, os.path.splitext(fname)[0] + '.json')
+
+            if os.path.exists(label_path):
+                shutil.copy(image_path, os.path.join(output_dir, fname))
+                shutil.copy(label_path, os.path.join(output_dir, os.path.basename(label_path)))
+                process_file_separately(image_path, label_path, output_dir, transforms)
+                count += 1
+            else:
+                print(f"跳过 {fname},缺少对应 JSON 文件")
+
+    print(f"\n处理完成,共处理 {count} 个样本")
+    print(f"输出路径:{os.path.abspath(output_dir)}")
+
+
+if __name__ == "__main__":
+    input_dir = r"G:\python_ws_g\data\total"
+    # input_dir = r"G:\python_ws_g\data\total"
+    parent_dir = os.path.dirname(input_dir)
+    output_dir = os.path.join(parent_dir, "a_strong_output")
+    process_dataset(input_dir, output_dir, config)

+ 254 - 0
utils/data_process/zyh/simplify_process_and_enhance_data/b_process_json.py

@@ -0,0 +1,254 @@
+import os
+import json
+from itertools import combinations
+import cv2
+import numpy as np
+import skimage
+from scipy.ndimage import zoom
+from tqdm import tqdm  # 添加 tqdm 库
+
+def to_int(x):
+    return tuple(map(int, x))
+
+def convert_labelme_to_custom_json(labelme_json_path):
+    """
+    Convert LabelMe JSON to custom JSON format.
+    """
+    try:
+        # Read LabelMe JSON file
+        with open(labelme_json_path, 'r', encoding='utf-8') as f:
+            labelme_data = json.load(f)
+        # 检查必要的键是否存在
+        if 'imageHeight' not in labelme_data:
+            raise KeyError("Missing 'imageHeight' in JSON")
+        if 'imageWidth' not in labelme_data:
+            raise KeyError("Missing 'imageWidth' in JSON")
+        # Extract image information
+        image_height = labelme_data['imageHeight']
+        image_width = labelme_data['imageWidth']
+        # Extract line segment information
+        lines = []
+        for shape in labelme_data.get('shapes', []):
+            if shape.get('label') == 'dseam1':
+                if len(shape.get('points', [])) != 2:
+                    print(f"Warning: Skipping invalid line in {labelme_json_path}")
+                    continue
+                start_point = shape['points'][0]
+                end_point = shape['points'][1]
+                lines.append([start_point, end_point])
+        # 检查是否有线段
+        if not lines:
+            raise ValueError("No line segments found in the JSON")
+        # Create custom JSON data
+        custom_json_data = {
+            "image_id": os.path.splitext(os.path.basename(labelme_json_path))[0],  # 使用 JSON 文件名作为 image_id
+            # "height": image_height,
+            # "width": image_width,
+            "lines": lines
+        }
+        return custom_json_data
+    except json.JSONDecodeError:
+        print(f"JSON Decode Error: Invalid JSON file - {labelme_json_path}")
+    except KeyError as e:
+        print(f"Key Error in {labelme_json_path}: {e}")
+    except ValueError as e:
+        print(f"Value Error in {labelme_json_path}: {e}")
+    except Exception as e:
+        print(f"Unexpected error processing {labelme_json_path}: {e}")
+    return None
+
+def find_image_file(json_path, image_extensions=('.jpg', '.jpeg', '.png', '.bmp')):
+    """
+    Find the corresponding image file for a given JSON file.
+    """
+    base_name = os.path.splitext(json_path)[0]
+    for ext in image_extensions:
+        image_path = f"{base_name}{ext}"
+        if os.path.exists(image_path):
+            return image_path
+    return None
+
+def generate_heatmap_data(image, lines):
+    """
+    Generate heatmap data from wireframe lines (in memory).
+    """
+    try:
+        im_rescale = (512, 512)
+        heatmap_scale = (128, 128)
+        fy, fx = heatmap_scale[1] / image.shape[0], heatmap_scale[0] / image.shape[1]
+        jmap = np.zeros((1,) + heatmap_scale, dtype=np.float32)
+        joff = np.zeros((1, 2) + heatmap_scale, dtype=np.float32)
+        lmap = np.zeros(heatmap_scale, dtype=np.float32)
+        lines[:, :, 0] = np.clip(lines[:, :, 0] * fx, 0, heatmap_scale[0] - 1e-4)
+        lines[:, :, 1] = np.clip(lines[:, :, 1] * fy, 0, heatmap_scale[1] - 1e-4)
+        lines = lines[:, :, ::-1]
+        junc = []
+        jids = {}
+        def jid(jun):
+            jun = tuple(jun[:2])
+            if jun in jids:
+                return jids[jun]
+            jids[jun] = len(junc)
+            junc.append(np.array(jun + (0,)))
+            return len(junc) - 1
+        lnid = []
+        lpos, lneg = [], []
+        for v0, v1 in lines:
+            lnid.append((jid(v0), jid(v1)))
+            lpos.append([junc[jid(v0)], junc[jid(v1)]])
+            vint0, vint1 = to_int(v0), to_int(v1)
+            jmap[0][vint0] = 1
+            jmap[0][vint1] = 1
+            rr, cc, value = skimage.draw.line_aa(*to_int(v0), *to_int(v1))
+            lmap[rr, cc] = np.maximum(lmap[rr, cc], value)
+        for v in junc:
+            vint = to_int(v[:2])
+            joff[0, :, vint[0], vint[1]] = v[:2] - vint - 0.5
+        llmap = zoom(lmap, [0.5, 0.5])
+        lineset = set([frozenset(l) for l in lnid])
+        # Generate negative samples
+        for i0, i1 in combinations(range(len(junc)), 2):
+            if frozenset([i0, i1]) not in lineset:
+                v0, v1 = junc[i0], junc[i1]
+                vint0, vint1 = to_int(v0[:2] / 2), to_int(v1[:2] / 2)
+                rr, cc, value = skimage.draw.line_aa(*vint0, *vint1)
+                lneg.append([v0, v1, i0, i1, np.average(np.minimum(value, llmap[rr, cc]))])
+        # 如果没有负样本,模拟生成一些负样本
+        if len(lneg) == 0:
+            print("Warning: No negative samples found. Simulating artificial negative samples.")
+            for i0, i1 in combinations(range(len(junc)), 2):
+                v0, v1 = junc[i0], junc[i1]
+                vint0, vint1 = to_int(v0[:2] / 2), to_int(v1[:2] / 2)
+                rr, cc, value = skimage.draw.line_aa(*vint0, *vint1)
+                simulated_value = np.random.uniform(0.01, 0.1)  # 模拟低置信度值
+                lneg.append([v0, v1, i0, i1, simulated_value])
+        assert len(lneg) != 0, "Failed to generate negative samples even after simulation."
+        lneg.sort(key=lambda l: -l[-1])
+        junc = np.array(junc, dtype=np.float32)
+        Lpos = np.array(lnid, dtype=np.int32)
+        Lneg = np.array([l[2:4] for l in lneg][:4000], dtype=np.int32)
+        lpos = np.array(lpos, dtype=np.float32)
+        lneg = np.array([l[:2] for l in lneg[:2000]], dtype=np.float32)
+        image = cv2.resize(image, im_rescale)
+        # Organize data into the target format
+        wires_data = {
+            "junc_map": {
+                "name": "junc_map",
+                "shape": list(jmap.shape),
+                "content": jmap.tolist()
+            },
+            "junc_offset": {
+                "name": "junc_offset",
+                "shape": list(joff.shape),
+                "content": joff.tolist()
+            },
+            "line_map": {
+                "name": "line_map",
+                "shape": list(lmap.shape),
+                "content": lmap.tolist()
+            },
+            "junc_coords": {
+                "name": "junc_coords",
+                "shape": list(junc.shape),
+                "content": junc.tolist()
+            },
+            "line_pos_idx": {
+                "name": "line_pos_idx",
+                "shape": list(Lpos.shape),
+                "content": Lpos.tolist()
+            },
+            "line_neg_idx": {
+                "name": "line_neg_idx",
+                "shape": list(Lneg.shape),
+                "content": Lneg.tolist()
+            },
+            "line_pos_coords": {
+                "name": "line_pos_coords",
+                "shape": list(lpos.shape),
+                "content": lpos.tolist()
+            },
+            "line_neg_coords": {
+                "name": "line_neg_coords",
+                "shape": list(lneg.shape),
+                "content": lneg.tolist()
+            }
+        }
+        return wires_data
+    except Exception as e:
+        print(f"Error generating heatmap data: {e}")
+        raise
+def batch_process(labelme_json_dir, output_dir):
+    """
+    Batch process LabelMe JSON files to generate custom JSONs and copy images.
+    """
+    json_files = [f for f in os.listdir(labelme_json_dir) if f.endswith('.json')]
+    total_files = len(json_files)
+    processed_files = 0
+    error_files = 0
+    os.makedirs(output_dir, exist_ok=True)
+    for filename in tqdm(json_files, desc="Processing JSON files", unit="file"):
+        labelme_json_path = os.path.join(labelme_json_dir, filename)
+        try:
+            # Step 1: Convert LabelMe JSON to custom JSON
+            custom_json_data = convert_labelme_to_custom_json(labelme_json_path)
+            if custom_json_data is None:
+                error_files += 1
+                continue
+            # Step 2: Find the corresponding image file
+            image_path = find_image_file(labelme_json_path)
+            if not image_path or not os.path.exists(image_path):
+                print(f"Error: Image file not found for {labelme_json_path}")
+                error_files += 1
+                continue
+            # Step 2.5: Copy image to output directory
+            image_filename = os.path.basename(image_path)
+            output_image_path = os.path.join(output_dir, image_filename)
+            if not os.path.exists(output_image_path):
+                try:
+                    import shutil
+                    shutil.copy(image_path, output_image_path)
+                except Exception as e:
+                    print(f"Error copying image file {image_path} to {output_image_path}: {e}")
+                    error_files += 1
+                    continue
+            # Step 3: Read and process image
+            image = cv2.imread(image_path)
+            if image is None:
+                print(f"Error: Unable to read image file - {image_path}")
+                error_files += 1
+                continue
+            # Step 4: Generate heatmap data (in memory)
+            lines = np.array(custom_json_data['lines']).reshape(-1, 2, 2)
+            wires_data = generate_heatmap_data(image, lines)
+            custom_json_data.pop("lines", None)  # Remove 'lines' field
+            # Step 5: Combine custom JSON and heatmap data
+            custom_json_data.update({
+                "image_id": custom_json_data["image_id"],
+                "boxes": [],
+                "segmentations": [],
+                "wires": [wires_data]
+            })
+            # Step 6: Save the final custom JSON (compact format)
+            output_json_path = os.path.join(output_dir, os.path.splitext(filename)[0] + '.json')
+            with open(output_json_path, 'w', encoding='utf-8') as f:
+                json.dump(custom_json_data, f, separators=(',', ':'))
+            print(f"Saved compact JSON file: {output_json_path}")
+            processed_files += 1
+        except Exception as e:
+            print(f"Unexpected error processing {labelme_json_path}: {e}")
+            error_files += 1
+    print(f"\nProcessing Complete:")
+    print(f"Total Files: {total_files}")
+    print(f"Processed Files: {processed_files}")
+    print(f"Error Files: {error_files}")
+
+
+
+
+
+if __name__ == "__main__":
+    labelme_json_dir = r"G:\python_ws_g\data\a_strong_output"
+    parent_dir = os.path.dirname(labelme_json_dir)
+    custom_json_output_dir = os.path.join(parent_dir, "a_strong_json_output")
+
+    batch_process(labelme_json_dir, custom_json_output_dir)

+ 47 - 0
utils/data_process/zyh/simplify_process_and_enhance_data/c_pic_to_512.py

@@ -0,0 +1,47 @@
+import os
+import cv2
+import shutil
+
+def resize_images(input_dir, output_dir, size=(512, 512)):
+    """
+    调整输入目录中的所有图像大小,并将图像和 JSON 一起保存到输出目录。
+
+    :param input_dir: 输入图像目录路径
+    :param output_dir: 输出图像目录路径
+    :param size: 目标图像大小,默认为 (512, 512)
+    """
+    # 创建输出目录
+    os.makedirs(output_dir, exist_ok=True)
+
+    # 遍历所有文件
+    for filename in os.listdir(input_dir):
+        input_path = os.path.join(input_dir, filename)
+        output_path = os.path.join(output_dir, filename)
+
+        # 如果是图像文件,执行缩放
+        if filename.lower().endswith(('.jpg', '.jpeg', '.png', '.bmp', '.gif', '.tiff')):
+            try:
+                img = cv2.imread(input_path)
+                if img is not None:
+                    resized_img = cv2.resize(img, size, interpolation=cv2.INTER_NEAREST)
+                    cv2.imwrite(output_path, resized_img)
+                    print(f"Resized and saved image: {output_path}")
+                else:
+                    print(f"Warning: Image is None - {filename}")
+            except Exception as e:
+                print(f"Error processing image {filename}: {e}")
+
+        # 如果是 JSON 文件,直接复制
+        elif filename.lower().endswith('.json'):
+            try:
+                shutil.copy2(input_path, output_path)
+                print(f"Copied JSON file: {output_path}")
+            except Exception as e:
+                print(f"Error copying JSON {filename}: {e}")
+
+# 使用示例
+input_directory = r"G:\python_ws_g\data\a_strong_json_output"
+parent_dir = os.path.dirname(input_directory)
+output_directory = os.path.join(parent_dir, "a_strong_512")
+
+resize_images(input_directory, output_directory)

+ 102 - 0
utils/data_process/zyh/simplify_process_and_enhance_data/d_data_spliter.py

@@ -0,0 +1,102 @@
+"""
+该脚本将源目录下同名的 .json 和 .diff 文件配对,
+并按如下结构将其整理至目标目录:
+
+输出结构如下:
+
+output_dir/
+├── images/
+│   ├── train/
+│   └── val/
+└── labels/
+    ├── train/
+    └── val/
+
+其中:
+- images/train 和 val 存放的是 `.diff` 文件
+- labels/train 和 val 存放的是 `.json` 文件
+"""
+
+import os
+import shutil
+import random
+from pathlib import Path
+
+def organize_data(
+    src_dir,
+    dest_dir,
+    image_extensions=['.tiff'],
+    label_extensions=['.json'],
+    val_ratio=0.2
+):
+    src_dir = Path(src_dir)
+    dest_dir = Path(dest_dir)
+
+    image_dir = dest_dir / 'images'
+    label_dir = dest_dir / 'labels'
+
+    # 创建文件夹结构
+    for split in ['train', 'val']:
+        (image_dir / split).mkdir(parents=True, exist_ok=True)
+        (label_dir / split).mkdir(parents=True, exist_ok=True)
+
+    # 获取所有文件
+    files = list(src_dir.glob('*'))
+    name_to_files = {}
+
+    # 分组:同名文件归为一组
+    for f in files:
+        stem = f.stem
+        name_to_files.setdefault(stem, []).append(f)
+
+    # 筛选出同时包含 label 和 image 的样本
+    paired_samples = []
+    for name, file_group in name_to_files.items():
+        image_file = next((f for f in file_group if f.suffix in image_extensions), None)
+        label_file = next((f for f in file_group if f.suffix in label_extensions), None)
+        if image_file and label_file:
+            paired_samples.append((image_file, label_file))
+        else:
+            print(f"⚠️ Skipping unpaired files for: {name}")
+
+    # 打乱并划分
+    random.shuffle(paired_samples)
+    split_idx = int(len(paired_samples) * (1 - val_ratio))
+    train_samples = paired_samples[:split_idx]
+    val_samples = paired_samples[split_idx:]
+
+    # 拷贝函数
+    def copy_samples(samples, split):
+        for img, lbl in samples:
+            shutil.copy(img, image_dir / split / img.name)
+            shutil.copy(lbl, label_dir / split / lbl.name)
+
+    # 执行拷贝
+    copy_samples(train_samples, 'train')
+    copy_samples(val_samples, 'val')
+
+    print(f"\n✅ Done! Processed {len(paired_samples)} pairs.")
+    print(f"Train: {len(train_samples)}, Val: {len(val_samples)}")
+
+if __name__ == "__main__":
+    # 输入输出目录(可修改)
+    source_dir = r"G:\python_ws_g\data\color"
+
+    parent_dir = os.path.dirname(source_dir)
+    output_dir = os.path.join(parent_dir, "a_dataset")
+
+    # 后缀名列表,方便以后扩展其他格式
+    image_exts = ['.tiff','.jpg']
+    label_exts = ['.json']
+
+    organize_data(source_dir, output_dir, image_exts, label_exts)
+
+
+
+
+
+
+
+
+
+

+ 88 - 0
utils/data_process/zyh/source_color.py

@@ -0,0 +1,88 @@
+import cv2
+import numpy as np
+import imageio
+import open3d as o3d
+from tifffile import tifffile
+# 相机内参矩阵 [fx, 0, cx; 0, fy, cy; 0, 0, 1]
+K = np.array([
+    [1.30449e3, 0,         5.2602e2],
+    [0,         1.30449e3, 1.07432e3],
+    [0,         0,         1]
+])
+fx, fy = K[0, 0], K[1, 1]
+cx, cy = K[0, 2], K[1, 2]
+
+def pointscloud2depthmap(points):
+    # 初始化一个空的目标数组
+    point_image = np.zeros((height, width, 3), dtype=np.float32)
+    # 遍历点云中的每个点,进行投影并填充目标数组
+    for point in points:
+        X, Y, Z ,r,g,b= point
+        if Z > 0:  # 确保Z值有效
+            # 计算2D图像坐标
+            u = int((X * fx) / Z + cx)
+            v = int((Y * fy) / Z + cy)
+
+            # 检查是否在图像边界内
+            if 0 <= u < width and 0 <= v < height:
+                point_image[v, u, :] = point
+
+    return point_image
+def pointscloud2colorimg(points):
+    # 初始化一个空的目标数组
+    point_image = np.zeros((height, width, 3), dtype=np.float32)
+    color_image = np.zeros((height, width, 3), dtype=np.float32)
+    # 遍历点云中的每个点,进行投影并填充目标数组
+    for point in points:
+        X, Y, Z ,r,g,b= point
+        if Z > 0:  # 确保Z值有效
+            # 计算2D图像坐标
+            u = int((X * fx) / Z + cx)
+            v = int((Y * fy) / Z + cy)
+
+            # 检查是否在图像边界内
+            if 0 <= u < width and 0 <= v < height:
+                # point_image[v, u, :] = point
+                color_image[v,u]=[r*255,g*255,b*255]
+
+    return color_image
+
+
+
+# 加载PCD文件
+pcd = o3d.io.read_point_cloud(r"F:\test_pointcloud\color.pcd")
+
+# 打印点的数量
+print("Number of points:", len(pcd.points))
+
+# 获取点云数据
+points = np.asarray(pcd.points)
+
+colors=np.asarray(pcd.colors)
+points = np.hstack((points, colors))  # (N, 6)
+
+# 打印前5个点的坐标
+print("First 5 points:\n", points[:5])
+print(f'color:{colors}')
+
+# 目标深度图尺寸
+height, width = 2000, 2000
+
+point_image=pointscloud2colorimg(points)
+# 打印结果以验证
+print("Shape of the projected point cloud:", point_image.shape)
+print("First few pixels (if any):", point_image[:5, :5, :])
+
+# 提取 Z 值作为深度图
+depth_map = point_image[:, :, 2]
+
+# 打印深度图的一些信息以验证
+print("Depth map shape:", depth_map.shape)
+print("Depth map dtype:", depth_map.dtype)
+print("Min depth value:", np.min(depth_map))
+print("Max depth value:", np.max(depth_map))
+
+# 保存为 TIFF 文件
+color_img_path='color_img.jpg'
+
+cv2.imwrite(color_img_path,point_image)

+ 167 - 0
utils/data_process/zyh/tiff_change_pic_reshape.py

@@ -0,0 +1,167 @@
+import os
+import json
+from itertools import combinations
+import numpy as np
+import skimage
+from scipy.ndimage import zoom
+from tqdm import tqdm
+import imageio.v3 as iio
+
+def to_int(x):
+    return tuple(map(int, x))
+
+def convert_labelme_to_custom_json(labelme_json_path):
+    try:
+        with open(labelme_json_path, 'r', encoding='utf-8') as f:
+            labelme_data = json.load(f)
+        if 'imageHeight' not in labelme_data:
+            raise KeyError("Missing 'imageHeight' in JSON")
+        if 'imageWidth' not in labelme_data:
+            raise KeyError("Missing 'imageWidth' in JSON")
+        image_height = labelme_data['imageHeight']
+        image_width = labelme_data['imageWidth']
+        lines = []
+        for shape in labelme_data.get('shapes', []):
+            if shape.get('label') == 'dseam1':
+                if len(shape.get('points', [])) != 2:
+                    print(f"Warning: Skipping invalid line in {labelme_json_path}")
+                    continue
+                start_point = shape['points'][0]
+                end_point = shape['points'][1]
+                lines.append([start_point, end_point])
+        if not lines:
+            raise ValueError("No line segments found in the JSON")
+        custom_json_data = {
+            "image_id": os.path.splitext(os.path.basename(labelme_json_path))[0],
+            "lines": lines
+        }
+        return custom_json_data
+    except Exception as e:
+        print(f"Error reading {labelme_json_path}: {e}")
+        return None
+
+def find_image_file(json_path, image_extensions=('.jpg', '.jpeg', '.png', '.bmp', '.tiff')):
+    base_name = os.path.splitext(json_path)[0]
+    for ext in image_extensions:
+        image_path = f"{base_name}{ext}"
+        if os.path.exists(image_path):
+            return image_path
+    return None
+
+def generate_heatmap_data(image, lines):
+    try:
+        im_rescale = (512, 512)
+        heatmap_scale = (128, 128)
+        fy, fx = heatmap_scale[1] / image.shape[0], heatmap_scale[0] / image.shape[1]
+        jmap = np.zeros((1,) + heatmap_scale, dtype=np.float32)
+        joff = np.zeros((1, 2) + heatmap_scale, dtype=np.float32)
+        lmap = np.zeros(heatmap_scale, dtype=np.float32)
+        lines[:, :, 0] = np.clip(lines[:, :, 0] * fx, 0, heatmap_scale[0] - 1e-4)
+        lines[:, :, 1] = np.clip(lines[:, :, 1] * fy, 0, heatmap_scale[1] - 1e-4)
+        lines = lines[:, :, ::-1]
+        junc = []
+        jids = {}
+        def jid(jun):
+            jun = tuple(jun[:2])
+            if jun in jids:
+                return jids[jun]
+            jids[jun] = len(junc)
+            junc.append(np.array(jun + (0,)))
+            return len(junc) - 1
+        lnid = []
+        lpos, lneg = [], []
+        for v0, v1 in lines:
+            lnid.append((jid(v0), jid(v1)))
+            lpos.append([junc[jid(v0)], junc[jid(v1)]])
+            vint0, vint1 = to_int(v0), to_int(v1)
+            jmap[0][vint0] = 1
+            jmap[0][vint1] = 1
+            rr, cc, value = skimage.draw.line_aa(*to_int(v0), *to_int(v1))
+            lmap[rr, cc] = np.maximum(lmap[rr, cc], value)
+        for v in junc:
+            vint = to_int(v[:2])
+            joff[0, :, vint[0], vint[1]] = v[:2] - vint - 0.5
+        llmap = zoom(lmap, [0.5, 0.5])
+        lineset = set([frozenset(l) for l in lnid])
+        for i0, i1 in combinations(range(len(junc)), 2):
+            if frozenset([i0, i1]) not in lineset:
+                v0, v1 = junc[i0], junc[i1]
+                vint0, vint1 = to_int(v0[:2] / 2), to_int(v1[:2] / 2)
+                rr, cc, value = skimage.draw.line_aa(*vint0, *vint1)
+                lneg.append([v0, v1, i0, i1, np.average(np.minimum(value, llmap[rr, cc]))])
+        if len(lneg) == 0:
+            for i0, i1 in combinations(range(len(junc)), 2):
+                v0, v1 = junc[i0], junc[i1]
+                vint0, vint1 = to_int(v0[:2] / 2), to_int(v1[:2] / 2)
+                rr, cc, value = skimage.draw.line_aa(*vint0, *vint1)
+                simulated_value = np.random.uniform(0.01, 0.1)
+                lneg.append([v0, v1, i0, i1, simulated_value])
+        lneg.sort(key=lambda l: -l[-1])
+        junc = np.array(junc, dtype=np.float32)
+        Lpos = np.array(lnid, dtype=np.int32)
+        Lneg = np.array([l[2:4] for l in lneg][:4000], dtype=np.int32)
+        lpos = np.array(lpos, dtype=np.float32)
+        lneg = np.array([l[:2] for l in lneg[:2000]], dtype=np.float32)
+        return {
+            "junc_map": {"name": "junc_map", "shape": list(jmap.shape), "content": jmap.tolist()},
+            "junc_offset": {"name": "junc_offset", "shape": list(joff.shape), "content": joff.tolist()},
+            "line_map": {"name": "line_map", "shape": list(lmap.shape), "content": lmap.tolist()},
+            "junc_coords": {"name": "junc_coords", "shape": list(junc.shape), "content": junc.tolist()},
+            "line_pos_idx": {"name": "line_pos_idx", "shape": list(Lpos.shape), "content": Lpos.tolist()},
+            "line_neg_idx": {"name": "line_neg_idx", "shape": list(Lneg.shape), "content": Lneg.tolist()},
+            "line_pos_coords": {"name": "line_pos_coords", "shape": list(lpos.shape), "content": lpos.tolist()},
+            "line_neg_coords": {"name": "line_neg_coords", "shape": list(lneg.shape), "content": lneg.tolist()}
+        }
+    except Exception as e:
+        print(f"Error generating heatmap data: {e}")
+        raise
+
+def batch_process(labelme_json_dir, output_dir):
+    json_files = [f for f in os.listdir(labelme_json_dir) if f.endswith('.json')]
+    os.makedirs(output_dir, exist_ok=True)
+    for filename in tqdm(json_files, desc="Processing JSON files"):
+        labelme_json_path = os.path.join(labelme_json_dir, filename)
+        try:
+            custom_json_data = convert_labelme_to_custom_json(labelme_json_path)
+            if custom_json_data is None:
+                continue
+            image_path = find_image_file(labelme_json_path)
+            if not image_path or not os.path.exists(image_path):
+                print(f"Image not found: {labelme_json_path}")
+                continue
+            image = iio.imread(image_path)
+            if image.ndim == 2:
+                image = image[:, :, np.newaxis]
+            elif image.ndim == 3 and image.shape[2] == 4:
+                image = image[:, :, :3]  # Drop alpha if needed
+
+            # 保存缩放后 TIFF(保持原始位深度)
+            zoom_factors = [512 / image.shape[0], 512 / image.shape[1], 1]
+
+            resized_image = zoom(image, zoom_factors, order=0)
+            
+            resized_image = resized_image.astype(image.dtype)
+            image_save_path = os.path.join(output_dir, os.path.splitext(filename)[0] + '.tiff')
+            iio.imwrite(image_save_path, resized_image)
+
+            # heatmap 处理
+            lines = np.array(custom_json_data['lines']).reshape(-1, 2, 2)
+            wires_data = generate_heatmap_data(image, lines)
+
+            # 输出 JSON 文件
+            custom_json_data.pop("lines", None)
+            custom_json_data.update({
+                "boxes": [],
+                "segmentations": [],
+                "wires": [wires_data]
+            })
+            output_json_path = os.path.join(output_dir, os.path.splitext(filename)[0] + '.json')
+            with open(output_json_path, 'w', encoding='utf-8') as f:
+                json.dump(custom_json_data, f, separators=(',', ':'))
+        except Exception as e:
+            print(f"Error processing {labelme_json_path}: {e}")
+
+if __name__ == "__main__":
+    labelme_json_dir = r"\\192.168.50.222\share\zyh\512\init\paomo"
+    custom_json_output_dir = r"\\192.168.50.222\share\zyh\512\init\target"
+    batch_process(labelme_json_dir, custom_json_output_dir)

+ 70 - 0
utils/data_process/zyh/toeveryone.py

@@ -0,0 +1,70 @@
+import os
+import shutil
+import math
+
+def split_folder_by_extension(input_folder, output_base_folder, parts=5, move=False, extensions=('.tiff', '.jpg')):
+    """
+    将指定文件夹中的文件(支持指定扩展名)按比例分成若干份保存到子文件夹中。
+
+    参数:
+    - input_folder: 原始文件夹路径
+    - output_base_folder: 输出的分组文件夹根路径
+    - parts: 要分成几份
+    - move: 是否移动文件(True 为移动,False 为复制)
+    - extensions: 要处理的文件扩展名组成的元组,例如 ('.tiff', '.jpg')
+    """
+
+    # 获取符合扩展名的所有文件
+    files = [f for f in os.listdir(input_folder) if f.lower().endswith(extensions)]
+    total_files = len(files)
+
+    if total_files == 0:
+        print(f"No files found with extensions {extensions}")
+        return
+
+    # 排序确保一致性
+    files.sort()
+
+    # 每组的文件数
+    files_per_group = math.ceil(total_files / parts)
+
+    print(f"Total files: {total_files}")
+    print(f"Splitting into {parts} parts, {files_per_group} files per part.")
+    print(f"Extensions included: {extensions}")
+
+    for i in range(parts):
+        part_folder = os.path.join(output_base_folder, f"part_{i}")
+        os.makedirs(part_folder, exist_ok=True)
+
+        start_idx = i * files_per_group
+        end_idx = min(start_idx + files_per_group, total_files)
+
+        for filename in files[start_idx:end_idx]:
+            src_path = os.path.join(input_folder, filename)
+            dst_path = os.path.join(part_folder, filename)
+
+            if move:
+                shutil.move(src_path, dst_path)
+            else:
+                shutil.copy2(src_path, dst_path)
+
+        print(f"Part {i}: {end_idx - start_idx} files saved to {part_folder}")
+
+
+# ✅ 示例调用
+if __name__ == '__main__':
+    # input_folder = r'G:\python_ws_g\data\pcd2color_result\color_jpg'
+    # output_base_folder = r'G:\python_ws_g\data\average'
+    # input_folder = r'\\192.168.50.222\share\zyh\5月彩色钢板数据汇总\515下午 57\pcd2color_result\color_jpg'
+    input_folder = r'/data/share/zyh/master_dataset/circle/huayan_circle/day02/color'
+    # output_base_folder = r'\\192.168.50.222\share\zyh\5月彩色钢板数据汇总\515下午 57\average'
+    output_base_folder = r'/data/share/zyh/master_dataset/circle/huayan_circle/day02/average'
+
+    # 调用函数,自定义扩展名和分几份
+    split_folder_by_extension(
+        input_folder=input_folder,
+        output_base_folder=output_base_folder,
+        parts=8,
+        move=False,
+        extensions=('.tiff', '.jpg', '.png')  # 支持 .tiff 和 .jpg 文件
+    )

+ 112 - 0
utils/data_process/zyh/tuoyuan_rgb/a_longshort.py

@@ -0,0 +1,112 @@
+import json
+import os
+import cv2
+import shutil
+
+# 输入文件夹
+json_folder = r"\\192.168.50.222\share\zyh\data\via\via_json"
+image_folder = r"\\192.168.50.222\share\zqy\colorphoto_for_guanban"
+output_folder = r"\\192.168.50.222\share\zyh\data\rgb_tuoyuan_axis_bbox\source"
+
+os.makedirs(output_folder, exist_ok=True)
+
+for json_file in os.listdir(json_folder):
+    if not json_file.lower().endswith(".json"):
+        continue
+
+    json_path = os.path.join(json_folder, json_file)
+
+    with open(json_path, "r", encoding="utf-8") as f:
+        data = json.load(f)
+
+    img_metadata = data.get("_via_img_metadata", {})
+
+    for img_info in img_metadata.values():
+        filename = img_info.get("filename")
+        regions = img_info.get("regions", [])
+
+        # 只取椭圆
+        ellipses = [r for r in regions if r.get("shape_attributes", {}).get("name") == "ellipse"]
+        if not ellipses:
+            continue
+
+        # 找图片路径
+        img_path = os.path.join(image_folder, filename)
+        if not os.path.exists(img_path):
+            print(f"图片未找到: {filename}")
+            continue
+
+        # 读取图片尺寸
+        img = cv2.imread(img_path)
+        if img is None:
+            print(f"图片无法读取: {filename}")
+            continue
+        height, width = img.shape[:2]
+
+        # 构造输出 JSON
+        output_data = {
+            "version": "5.0.1",
+            "flags": {},
+            "shapes": [],
+            "imagePath": filename,
+            "imageHeight": height,
+            "imageWidth": width
+        }
+
+        for e in ellipses:
+            cx = e["shape_attributes"]["cx"]
+            cy = e["shape_attributes"]["cy"]
+            rx = e["shape_attributes"]["rx"]
+            ry = e["shape_attributes"]["ry"]
+
+            # 四个端点
+            left = [cx - rx, cy]
+            right = [cx + rx, cy]
+            top = [cx, cy - ry]
+            bottom = [cx, cy + ry]
+
+            # bbox
+            xmin = min(left[0], right[0], top[0], bottom[0])
+            xmax = max(left[0], right[0], top[0], bottom[0])
+            ymin = min(left[1], right[1], top[1], bottom[1])
+            ymax = max(left[1], right[1], top[1], bottom[1])
+
+            # 长轴(水平)
+            long_axis = [left, right]
+            # 短轴(垂直)
+            short_axis = [top, bottom]
+
+            # 保存长轴
+            output_data["shapes"].append({
+                "label": "long_axis",
+                "points": long_axis,
+                "shape_type": "line",
+                "flags": {},
+                "xmin": int(xmin),
+                "ymin": int(ymin),
+                "xmax": int(xmax),
+                "ymax": int(ymax)
+            })
+
+            # 保存短轴
+            output_data["shapes"].append({
+                "label": "short_axis",
+                "points": short_axis,
+                "shape_type": "line",
+                "flags": {},
+                "xmin": int(xmin),
+                "ymin": int(ymin),
+                "xmax": int(xmax),
+                "ymax": int(ymax)
+            })
+
+        # 保存 JSON
+        json_output_path = os.path.join(output_folder, filename.replace(".jpg", ".json"))
+        with open(json_output_path, "w", encoding="utf-8") as out_f:
+            json.dump(output_data, out_f, ensure_ascii=False, indent=4)
+
+        # 复制图片
+        img_output_path = os.path.join(output_folder, filename)
+        shutil.copy(img_path, img_output_path)
+
+        print(f"已生成: {json_output_path} 和 {img_output_path}")

+ 102 - 0
utils/data_process/zyh/tuoyuan_rgb/d_data_spliter.py

@@ -0,0 +1,102 @@
+"""
+该脚本将源目录下同名的 .json 和 .diff 文件配对,
+并按如下结构将其整理至目标目录:
+
+输出结构如下:
+
+output_dir/
+├── images/
+│   ├── train/
+│   └── val/
+└── labels/
+    ├── train/
+    └── val/
+
+其中:
+- images/train 和 val 存放的是 `.diff` 文件
+- labels/train 和 val 存放的是 `.json` 文件
+"""
+
+import os
+import shutil
+import random
+from pathlib import Path
+
+def organize_data(
+    src_dir,
+    dest_dir,
+    image_extensions=['.tiff'],
+    label_extensions=['.json'],
+    val_ratio=0.2
+):
+    src_dir = Path(src_dir)
+    dest_dir = Path(dest_dir)
+
+    image_dir = dest_dir / 'images'
+    label_dir = dest_dir / 'labels'
+
+    # 创建文件夹结构
+    for split in ['train', 'val']:
+        (image_dir / split).mkdir(parents=True, exist_ok=True)
+        (label_dir / split).mkdir(parents=True, exist_ok=True)
+
+    # 获取所有文件
+    files = list(src_dir.glob('*'))
+    name_to_files = {}
+
+    # 分组:同名文件归为一组
+    for f in files:
+        stem = f.stem
+        name_to_files.setdefault(stem, []).append(f)
+
+    # 筛选出同时包含 label 和 image 的样本
+    paired_samples = []
+    for name, file_group in name_to_files.items():
+        image_file = next((f for f in file_group if f.suffix in image_extensions), None)
+        label_file = next((f for f in file_group if f.suffix in label_extensions), None)
+        if image_file and label_file:
+            paired_samples.append((image_file, label_file))
+        else:
+            print(f"⚠️ Skipping unpaired files for: {name}")
+
+    # 打乱并划分
+    random.shuffle(paired_samples)
+    split_idx = int(len(paired_samples) * (1 - val_ratio))
+    train_samples = paired_samples[:split_idx]
+    val_samples = paired_samples[split_idx:]
+
+    # 拷贝函数
+    def copy_samples(samples, split):
+        for img, lbl in samples:
+            shutil.copy(img, image_dir / split / img.name)
+            shutil.copy(lbl, label_dir / split / lbl.name)
+
+    # 执行拷贝
+    copy_samples(train_samples, 'train')
+    copy_samples(val_samples, 'val')
+
+    print(f"\n✅ Done! Processed {len(paired_samples)} pairs.")
+    print(f"Train: {len(train_samples)}, Val: {len(val_samples)}")
+
+if __name__ == "__main__":
+    # 输入输出目录(可修改)
+    source_dir = r"\\192.168.50.222\share\zyh\data\rgb_tuoyuan_axis_bbox\source"
+
+    parent_dir = os.path.dirname(source_dir)
+    output_dir = os.path.join(parent_dir, "a_dataset")
+
+    # 后缀名列表,方便以后扩展其他格式
+    image_exts = ['.tiff','.jpg']
+    label_exts = ['.json']
+
+    organize_data(source_dir, output_dir, image_exts, label_exts)
+
+
+
+
+
+
+
+
+
+

+ 114 - 0
utils/data_process/zyh/xany2yolo/wyo.py

@@ -0,0 +1,114 @@
+import os
+import json
+import shutil
+from tqdm import tqdm
+
+# ======================
+# ⚙️ 可配置路径
+# ======================
+# 原始数据目录(Labelme)
+INPUT_DIR = r"\\192.168.50.222\share\zyh\data\坡口切割\10-11"
+
+# 输出 YOLO 数据集目录(标准格式)
+OUTPUT_DIR =r"\\192.168.50.222\share\zyh\data\坡口切割\yolo"
+
+# ======================
+# 🚀 核心函数
+# ======================
+
+def convert_labelme_to_yolo(json_path, output_txt, image_w, image_h):
+    """将 Labelme JSON 转为 YOLO txt"""
+    with open(json_path, "r", encoding="utf-8") as f:
+        data = json.load(f)
+
+    lines = []
+    for shape in data.get("shapes", []):
+        if shape["label"] != "line":
+            continue
+        points = shape["points"]
+        if len(points) != 2:
+            continue
+
+        x1, y1 = points[0]
+        x2, y2 = points[1]
+
+        # 归一化
+        x1 /= image_w
+        y1 /= image_h
+        x2 /= image_w
+        y2 /= image_h
+
+        lines.append(f"0 {x1:.6f} {y1:.6f} {x2:.6f} {y2:.6f}")
+
+    os.makedirs(os.path.dirname(output_txt), exist_ok=True)
+    with open(output_txt, "w", encoding="utf-8") as f:
+        f.write("\n".join(lines))
+
+
+def process_split(split):
+    """处理 train / val 子集"""
+    print(f"\n📂 处理中:{split} 集")
+
+    img_src_dir = os.path.join(INPUT_DIR, "images", split)
+    json_src_dir = os.path.join(INPUT_DIR, "labels", split)
+
+    img_dst_dir = os.path.join(OUTPUT_DIR, split, "images")
+    label_dst_dir = os.path.join(OUTPUT_DIR, split, "labels")
+
+    os.makedirs(img_dst_dir, exist_ok=True)
+    os.makedirs(label_dst_dir, exist_ok=True)
+
+    json_files = [f for f in os.listdir(json_src_dir) if f.endswith(".json")]
+
+    for json_file in tqdm(json_files):
+        json_path = os.path.join(json_src_dir, json_file)
+
+        with open(json_path, "r", encoding="utf-8") as f:
+            data = json.load(f)
+
+        image_name = data["imagePath"]
+        image_w = data["imageWidth"]
+        image_h = data["imageHeight"]
+
+        # 图片路径
+        src_img_path = os.path.join(img_src_dir, image_name)
+        dst_img_path = os.path.join(img_dst_dir, image_name)
+
+        if not os.path.exists(src_img_path):
+            print(f"⚠️ 缺失图片:{src_img_path}")
+            continue
+
+        # 拷贝图片
+        shutil.copy2(src_img_path, dst_img_path)
+
+        # 生成 YOLO txt
+        txt_name = os.path.splitext(image_name)[0] + ".txt"
+        txt_path = os.path.join(label_dst_dir, txt_name)
+
+        convert_labelme_to_yolo(json_path, txt_path, image_w, image_h)
+
+
+def generate_data_yaml():
+    """生成 data.yaml 文件"""
+    yaml_path = os.path.join(OUTPUT_DIR, "data.yaml")
+    yaml_content = f"""path: {OUTPUT_DIR}
+train: train/images
+val: val/images
+
+names:
+  0: line
+"""
+    with open(yaml_path, "w", encoding="utf-8") as f:
+        f.write(yaml_content)
+    print(f"\n✅ 已生成 data.yaml: {yaml_path}")
+
+
+if __name__ == "__main__":
+    print("🚀 开始转换 Labelme → YOLO 数据集 (标准格式)")
+
+    process_split("train")
+    process_split("val")
+
+    generate_data_yaml()
+
+    print("\n🎉 完成!YOLO 数据集已生成于:", OUTPUT_DIR)