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)
