import tkinter as tk
from tkinter import filedialog, messagebox
from tkinter import Toplevel
from PIL import Image, ImageTk
import numpy as np
import cv2
import os

img_path = ""  # 全局变量，用于存储图像路径
src = None  # 全局变量，用于存储已选择的图像
X = None  # 用于存储第一张图像
Y = None  # 用于存储第二张图像
img_label = None  # 全局变量，用于存储显示选择的图片的标签
edge = None

ThreWin = None
VergeWin = None
LineWin = None

def select_image(root):
    global img_path, src, img_label, edge

    img_path = filedialog.askopenfilename(filetypes=[("Image files", "*.jpg;*.png;*.jpeg;*.bmp")])
    if img_path:
        # 确保路径中的反斜杠正确处理，并使用 UTF-8 编码处理中文路径
        img_path_fixed = os.path.normpath(img_path)

        # 图像输入
        src_temp = cv2.imdecode(np.fromfile(img_path_fixed, dtype=np.uint8), cv2.IMREAD_UNCHANGED)
        if src_temp is None:
            messagebox.showerror("错误", "无法读取图片，请选择有效的图片路径")
            return
        src = cv2.cvtColor(src_temp, cv2.COLOR_BGR2RGB)

        # 检查 img_label 是否存在且有效
        if img_label is None or not img_label.winfo_exists():
            img_label = tk.Label(root)
            img_label.pack(side=tk.TOP, pady=10)

        img = Image.open(img_path)
        img.thumbnail((160, 160))
        img_tk = ImageTk.PhotoImage(img)
        img_label.configure(image=img_tk)
        img_label.image = img_tk

        # 定义 edge 变量为 PIL.Image 对象，以便稍后保存
        edge = Image.fromarray(src)
    else:
        messagebox.showerror("错误", "没有选择图片路径")

def show_selected_image(root):
    global img_label
    img_label = tk.Label(root)
    img_label.pack(side=tk.TOP, pady=10)
    img = Image.open(img_path)
    img.thumbnail((160, 160))
    img_tk = ImageTk.PhotoImage(img)
    img_label.configure(image=img_tk)
    img_label.image = img_tk

def changeSize(event, img, LabelPic):
    img_aspect = img.shape[1] / img.shape[0]
    new_aspect = event.width / event.height

    if new_aspect > img_aspect:
        new_width = int(event.height * img_aspect)
        new_height = event.height
    else:
        new_width = event.width
        new_height = int(event.width / img_aspect)

    resized_image = cv2.resize(img, (new_width, new_height))
    image1 = ImageTk.PhotoImage(Image.fromarray(resized_image))
    LabelPic.image = image1
    LabelPic['image'] = image1

def savefile():
    global edge

    filename = filedialog.asksaveasfilename(defaultextension=".jpg", filetypes=[("JPEG files", "*.jpg"), ("PNG files", "*.png"), ("BMP files", "*.bmp")])
    if not filename:
        return
    # 确保 edge 变量已定义
    if edge is not None:
        try:
            edge.save(filename)
            messagebox.showinfo("保存成功", "图片保存成功！")
        except Exception as e:
            messagebox.showerror("保存失败", f"无法保存图片: {e}")
    else:
        messagebox.showerror("保存失败", "没有图像可保存")

#阈值化
def threshold(root):
    global src, ThreWin, edge

    # 判断是否已经选取图片
    if src is None:
        messagebox.showerror("错误", "没有选择图片！")
        return

    # 转变图像为灰度图
    gray = cv2.cvtColor(src, cv2.COLOR_BGR2GRAY)

    #TRIANGLE自适应阈值
    ret, TRIANGLE_img = cv2.threshold(gray, 0, 255, cv2.THRESH_TRIANGLE)

    #OTSU自适应阈值
    ret, OTSU_img = cv2.threshold(gray, 0, 255, cv2.THRESH_OTSU)

    #TRUNC截断阈值(200)
    ret, TRUNC_img = cv2.threshold(gray, 200, 255, cv2.THRESH_TRUNC)

    #TOZERO归零阈值(100)
    ret, TOZERO__img = cv2.threshold(gray, 100, 255, cv2.THRESH_TOZERO)

    combined = np.hstack((TRIANGLE_img, OTSU_img, TRUNC_img, TOZERO__img))
    # 更新 edge 变量
    edge = Image.fromarray(combined)

    # 创建Toplevel窗口
    try:
        ThreWin.destroy()
    except Exception as e:
        print("NVM")
    finally:
        ThreWin = Toplevel()
        ThreWin.attributes('-topmost', True)
    ThreWin.geometry("720x300")
    ThreWin.resizable(True, True)  # 可缩放
    ThreWin.title("阈值化结果")

    # 显示图像
    LabelPic = tk.Label(ThreWin, text="IMG", width=720, height=240)
    image = ImageTk.PhotoImage(Image.fromarray(combined))
    LabelPic.image = image
    LabelPic['image'] = image

    LabelPic.bind('<Configure>', lambda event: changeSize(event, combined, LabelPic))
    LabelPic.pack(fill=tk.BOTH, expand=tk.YES)

    # 添加保存按钮
    btn_save = tk.Button(ThreWin, text="保存", bg='#add8e6', fg='black', font=('Helvetica', 14), width=20,
                         command=savefile)
    btn_save.pack(pady=10)

    return

#边缘检测
def verge(root):
    global src, VergeWin, edge

    # 判断是否已经选取图片
    if src is None:
        messagebox.showerror("错误", "没有选择图片！")
        return

    # 转变图像为灰度图
    grayImage = cv2.cvtColor(src, cv2.COLOR_BGR2GRAY)

    #1.Roberts算子
    kernelx = np.array([[-1, 0], [0, 1]], dtype=int)
    kernely = np.array([[0, -1], [1, 0]], dtype=int)
    #卷积操作
    x = cv2.filter2D(grayImage, cv2.CV_16S, kernelx)
    y = cv2.filter2D(grayImage, cv2.CV_16S, kernely)
    #数据格式转换
    absX = cv2.convertScaleAbs(x)
    absY = cv2.convertScaleAbs(y)
    Roberts = cv2.addWeighted(absX, 0.5, absY, 0.5, 0)

    #2.Sobel算子
    x = cv2.Sobel(grayImage, cv2.CV_16S, 1, 0)
    y = cv2.Sobel(grayImage, cv2.CV_16S, 0, 1)
    #数据格式转换
    absX = cv2.convertScaleAbs(x)
    absY = cv2.convertScaleAbs(y)
    #组合图像
    Sobel = cv2.addWeighted(absX, 0.5, absY, 0.5, 0)

    #3.拉普拉斯算法&高斯滤波
    gray = cv2.GaussianBlur(grayImage, (5, 5), 0, 0)
    dst = cv2.Laplacian(gray, cv2.CV_16S, ksize=3)
    #数据格式转换
    Laplacian = cv2.convertScaleAbs(dst)

    #4.LoG边缘算子&边缘扩充&高斯滤波
    gray = cv2.copyMakeBorder(grayImage, 2, 2, 2, 2, borderType=cv2.BORDER_REPLICATE)
    image = cv2.GaussianBlur(gray, (3, 3), 0, 0)
    #使用Numpy定义LoG算子
    m1 = np.array(
        [[0, 0, -1, 0, 0], [0, -1, -2, -1, 0], [-1, -2, 16, -2, -1], [0, -1, -2, -1, 0], [0, 0, -1, 0, 0]])
    #卷积运算
    rows = image.shape[0]
    cols = image.shape[1]
    image1 = np.zeros(image.shape)
    # 为了使卷积对每个像素都进行运算，原图像的边缘像素要对准模板的中心。
    # 由于图像边缘扩大了2像素，因此要从位置2到行(列)-2
    for i in range(2, rows - 2):
        for j in range(2, cols - 2):
            image1[i, j] = np.sum((m1 * image[i - 2:i + 3, j - 2:j + 3]))

    #数据格式转换
    Log = cv2.convertScaleAbs(image1)

    #5.Sobel算子
    image = cv2.GaussianBlur(grayImage, (3, 3), 0)
    #求x，y方向的Sobel算子
    gradx = cv2.Sobel(image, cv2.CV_16SC1, 1, 0)
    grady = cv2.Sobel(image, cv2.CV_16SC1, 0, 1)
    #使用Canny函数处理图像，x,y分别是3求出来的梯度，低阈值50，高阈值150
    edge_output = cv2.Canny(gradx, grady, 50, 150)

    Roberts = cv2.resize(Roberts, (grayImage.shape[1], grayImage.shape[0]))
    Sobel = cv2.resize(Sobel, (grayImage.shape[1], grayImage.shape[0]))
    Laplacian = cv2.resize(Laplacian, (grayImage.shape[1], grayImage.shape[0]))
    Log = cv2.resize(Log, (grayImage.shape[1], grayImage.shape[0]))
    edge_output = cv2.resize(edge_output, (grayImage.shape[1], grayImage.shape[0]))

    combined = np.hstack((Roberts, Sobel, Laplacian, Log, edge_output))

    # 更新 edge 变量
    edge = Image.fromarray(combined)

    # 创建Toplevel窗口
    try:
        VergeWin.destroy()
    except Exception as e:
        print("NVM")
    finally:
        VergeWin = Toplevel()
        VergeWin.attributes('-topmost', True)
    VergeWin.geometry("720x300")
    VergeWin.resizable(True, True)  # 可缩放
    VergeWin.title("边缘检测结果")

    # 显示图像
    LabelPic = tk.Label(VergeWin, text="IMG", width=720, height=240)
    image = ImageTk.PhotoImage(Image.fromarray(combined))
    LabelPic.image = image
    LabelPic['image'] = image

    LabelPic.bind('<Configure>', lambda event: changeSize(event, combined, LabelPic))
    LabelPic.pack(fill=tk.BOTH, expand=tk.YES)

    # 添加保存按钮
    btn_save = tk.Button(VergeWin, text="保存", bg='#add8e6', fg='black', font=('Helvetica', 14), width=20,
                         command=savefile)
    btn_save.pack(pady=10)
    return

#线条变化检测
def line_chan(root):
    global src, LineWin, edge

    # 判断是否已经选取图片
    if src is None:
        messagebox.showerror("错误", "没有选择图片！")
        return

    img = cv2.GaussianBlur(src, (3, 3), 0)
    edges = cv2.Canny(img, 50, 150, apertureSize=3)

    # 使用HoughLines算法
    lines = cv2.HoughLines(edges, 1, np.pi / 2, 118)
    result = img.copy()
    for i_line in lines:
        for line in i_line:
            rho = line[0]
            theta = line[1]
            if (theta < (np.pi / 4.)) or (theta > (3. * np.pi / 4.0)):  # 垂直直线
                pt1 = (int(rho / np.cos(theta)), 0)
                pt2 = (int((rho - result.shape[0] * np.sin(theta)) / np.cos(theta)), result.shape[0])
                cv2.line(result, pt1, pt2, (0, 0, 255))
            else:
                pt1 = (0, int(rho / np.sin(theta)))
                pt2 = (result.shape[1], int((rho - result.shape[1] * np.cos(theta)) / np.sin(theta)))
                cv2.line(result, pt1, pt2, (0, 0, 255), 1)

    # 使用HoughLinesP算法
    minLineLength = 200
    maxLineGap = 15
    linesP = cv2.HoughLinesP(edges, 1, np.pi / 180, 80, minLineLength, maxLineGap)
    result_P = img.copy()
    for i_P in linesP:
        for x1, y1, x2, y2 in i_P:
            cv2.line(result_P, (x1, y1), (x2, y2), (0, 255, 0), 3)

    combined = np.hstack((result, result_P))
    combined = cv2.cvtColor(combined, cv2.COLOR_BGR2RGB)
    # 更新 edge 变量
    edge = Image.fromarray(result)

    # 创建Toplevel窗口
    try:
        LineWin.destroy()
    except Exception as e:
        print("NVM")
    finally:
        LineWin = Toplevel()
        LineWin.attributes('-topmost', True)
    LineWin.geometry("720x300")
    LineWin.resizable(True, True)  # 可缩放
    LineWin.title("线条变化检测结果")

    # 显示图像
    LabelPic = tk.Label(LineWin, text="IMG", width=720, height=240)
    image = ImageTk.PhotoImage(Image.fromarray(cv2.cvtColor(combined, cv2.COLOR_BGR2RGB)))
    LabelPic.image = image
    LabelPic['image'] = image

    LabelPic.bind('<Configure>', lambda event: changeSize(event, combined, LabelPic))
    LabelPic.pack(fill=tk.BOTH, expand=tk.YES)

    # 添加保存按钮
    btn_save = tk.Button(LineWin, text="保存", bg='#add8e6', fg='black', font=('Helvetica', 14), width=20,
                         command=savefile)
    btn_save.pack(pady=10)
    return