import torch
import cv2
import numpy as np
import matplotlib.pyplot as plt

from linknet.main import LinkNet


# --- 首先，确保模型定义代码在这里 ---
# (你需要从之前的 main.py 文件中复制 LinkNet 和 DecoderBlock 类的定义)
# class DecoderBlock(nn.Module): ...
# class LinkNet(nn.Module): ...
# --- 假设模型定义代码已经复制过来了 ---


def predict_single_image(model_path, image_path, image_size=256):
    """
    加载训练好的LinkNet模型，对单张新图片进行预测。

    参数:
    - model_path (str): 已保存的模型权重文件路径 (.pth)。
    - image_path (str): 待预测的图片文件路径。
    - image_size (int): 模型训练时使用的图片尺寸。

    返回:
    - predicted_mask (numpy.ndarray): 预测出的二值化掩码图，与原图尺寸相同，
                                      像素值为0或255。
    - overlay_image (numpy.ndarray): 将预测掩码（红色）叠加在原图上的结果图。
    """
    # 1. 设备选择
    device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
    print(f"Using device: {device}")

    # 2. 加载模型结构并载入权重
    #   - 实例化你的模型结构
    #   - 使用 load_state_dict 载入已保存的权重
    #   - 将模型切换到评估模式 .eval()
    model = LinkNet(num_classes=1)
    model.load_state_dict(torch.load(model_path, map_location=device))
    model.to(device)
    model.eval()

    # 3. 加载并预处理图片
    #   - 读取图片（灰度模式）
    img_original = cv2.imread(image_path, cv2.IMREAD_GRAYSCALE)
    if img_original is None:
        raise FileNotFoundError(f"Image not found at {image_path}")

    original_h, original_w = img_original.shape

    #   - 缩放到模型需要的尺寸 (和训练时一样)
    img_resized = cv2.resize(img_original, (image_size, image_size))

    #   - 归一化 (和训练时一样)
    img_normalized = img_resized / 255.0

    #   - 增加批次和通道维度 (H, W) -> (1, 1, H, W)
    img_tensor = torch.from_numpy(img_normalized).unsqueeze(0).unsqueeze(0).float()

    #   - 将Tensor发送到设备
    img_tensor = img_tensor.to(device)

    # 4. 模型推理
    #    使用 torch.no_grad() 来关闭梯度计算，节省显存并加速
    with torch.no_grad():
        output = model(img_tensor)

    # 5. 后处理
    #   - 将输出Tensor转回Numpy数组
    #   - 去掉批次和通道维度 (1, 1, H, W) -> (H, W)
    pred_mask_resized = output.cpu().numpy()[0, 0]

    #   - 二值化：将概率图 (0-1) 转换为掩码图 (0或1)
    #     这里的 0.5 是阈值，可以根据实际情况调整
    pred_mask_binary = (pred_mask_resized > 0.5).astype(np.uint8)

    #   - 将掩码图恢复到原始图片的尺寸
    predicted_mask = cv2.resize(pred_mask_binary, (original_w, original_h),
                                interpolation=cv2.INTER_NEAREST) * 255

    # 6. (可选) 创建可视化叠加图
    overlay_image = cv2.cvtColor(img_original, cv2.COLOR_GRAY2BGR)
    overlay_image[predicted_mask == 255] = [0, 0, 255]  # 在焊缝位置标记为红色 (BGR)

    return predicted_mask, overlay_image


# --- 如何调用这个函数 ---
if __name__ == '__main__':
    # 从 main.py 中复制模型定义代码到这里
    # ...

    MODEL_FILE = 'best_linknet_up_model_line2.pth'
    IMAGE_TO_TEST = 'test/004/input/004.jpg'  # <--- 修改为你的图片路径

    try:
        # 调用预测函数
        final_mask, overlay = predict_single_image(MODEL_FILE, IMAGE_TO_TEST)

        # 显示结果
        # cv2.imshow('Original Image', cv2.imread(IMAGE_TO_TEST))
        # cv2.imshow('Predicted Mask', final_mask)
        # cv2.imshow('Overlay Result', overlay)
        #
        # # 按任意键退出
        # cv2.waitKey(0)
        # cv2.destroyAllWindows()

        # 也可以保存结果
        # cv2.imwrite('predicted_mask.png', final_mask)
        cv2.imwrite('overlay_result.png', overlay)

    except FileNotFoundError as e:
        print(e)
    except Exception as e:
        print(f"An error occurred: {e}")
