import os

import cv2
import numpy as np
import albumentations as A
import onnxruntime as ort


class Detection:
    def __init__(self):
        self.CLASS_NAMES = ["background", "Hollowing", "Water seepage", "Cracking"]
        self.PALETTE = np.array([[0, 0, 0], [255, 0, 0], [0, 255, 0], [0, 0, 255]], dtype=np.uint8)
        self.tfm = A.Compose([
            A.Resize(512, 512),
            A.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225]),
        ])
        real_path = os.path.join(os.path.dirname(__file__), "segformer.onnx")
        self.model = ort.InferenceSession(
            real_path,
            providers=["CUDAExecutionProvider", "CPUExecutionProvider"]
        )
        print("ONNX 模型加载完成！")

    def get_contours(self, mask_np):
        res = []
        for idx in range(1, len(self.CLASS_NAMES)):
            name = self.CLASS_NAMES[idx]
            binary = (mask_np == idx).astype(np.uint8) * 255
            cnts, _ = cv2.findContours(binary, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
            for c in cnts:
                if cv2.contourArea(c) < 20:
                    continue
                eps = 0.002 * cv2.arcLength(c, True)
                poly = cv2.approxPolyDP(c, eps, True)
                if len(poly) >= 3:
                    res.append((name, poly.reshape(-1, 2).tolist()))
        return res

    def detect(self, img_input):
        # 读取图片
        if isinstance(img_input, str):
            img_bgr = cv2.imdecode(np.fromfile(img_input, dtype=np.uint8), cv2.IMREAD_COLOR)
        else:
            img_bgr = img_input

        h, w = img_bgr.shape[:2]
        img_rgb = cv2.cvtColor(img_bgr, cv2.COLOR_BGR2RGB)

        # 数据预处理
        x = self.tfm(image=img_rgb)["image"]
        # 将 HWC -> CHW 并增加 batch 维度
        x = np.transpose(x, (2, 0, 1))  # HWC -> CHW
        x = np.expand_dims(x, axis=0).astype(np.float32)  # batch x C x H x W

        # ONNX 推理
        inp_name = self.model.get_inputs()[0].name
        out_name = self.model.get_outputs()[0].name
        out = self.model.run([out_name], {inp_name: x})[0]

        # 获取预测结果
        pred = out.argmax(axis=1)[0].astype(np.uint8)

        mask = cv2.resize(pred, (w, h), interpolation=cv2.INTER_NEAREST)
        mask_rgb = self.PALETTE[mask]
        coords = self.get_contours(mask)
        # print(coords)
        return mask_rgb, coords


class DetectionMock:
    def __init__(self):
        self.onnx_path = "segformer.onnx"

    def detect(self, img_input):
        if isinstance(img_input, str):
            img_bgr = cv2.imdecode(np.fromfile(img_input, dtype=np.uint8), cv2.IMREAD_COLOR)
        else:
            img_bgr = img_input

        img_rgb = cv2.cvtColor(img_bgr, cv2.COLOR_BGR2RGB)

        return img_rgb, [
            ('Cracking', [[771, 928], [757, 935]]),
            ('Cracking', [[254, 740], [251, 942]]),
            ('Cracking', [[764, 420], [764, 424]]),
            ('Cracking', [[257, 238], [251, 245]]),
            ('Cracking', [[1436, 145], [1401, 145]])
        ]


if __name__ == "__main__":
    img_path = "test.jpg"

    detection = Detection()
    mask_res, coords_res = detection.detect(img_path)

    print("Mask Shape:", mask_res.shape)
    print("Detections:", coords_res)
    cv2.imwrite("res_onnx.png", cv2.cvtColor(mask_res, cv2.COLOR_RGB2BGR))
