You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

334 lines
12 KiB

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)
# 使用 cv2.imdecode 加载图像,处理中文路径
src_temp = cv2.imdecode(np.fromfile(img_path_fixed, dtype=np.uint8), cv2.IMREAD_UNCHANGED)
if src_temp is None:
messagebox.showerror("错误", "无法读取图片,请选择有效的图片路径")
return
# 将图像从 BGR 转换为 RGB
src = cv2.cvtColor(src_temp, cv2.COLOR_BGR2RGB)
# 检查 img_label 是否存在且有效,如果不存在则创建新的 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)
# 使用 PIL 加载并缩放图像以适应标签大小
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)
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]])
image1 = np.zeros(image.shape)
rows = image.shape[0]
cols = image.shape[1]
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. Canny 边缘检测
image = cv2.GaussianBlur(grayImage, (3, 3), 0)
gradx = cv2.Sobel(image, cv2.CV_16SC1, 1, 0)
grady = cv2.Sobel(image, cv2.CV_16SC1, 0, 1)
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 变量为 PIL.Image 对象
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)
def line_chan(root):
"""
检测图像中的线条变化并显示结果
"""
global src, LineWin, edge
# 判断是否已经选取图片
if src is None:
messagebox.showerror("错误", "没有选择图片!")
return
# 使用高斯模糊和 Canny 边缘检测处理图像
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))
# 更新 edge 变量为 PIL.Image 对象
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)