diff --git a/Pwindow.py b/Pwindow.py new file mode 100644 index 0000000..51b2a5d --- /dev/null +++ b/Pwindow.py @@ -0,0 +1,969 @@ +import cv2 +import numpy as np +import random +import tkinter +import tkinter as tk +import matplotlib.pyplot as plt +import albumentations as A +from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg +from tkinter.messagebox import showinfo, showwarning +from tkinter import filedialog +from PIL import Image,ImageTk +from tkinter.filedialog import askopenfilename, askopenfilenames, asksaveasfilename, askdirectory + + +# ---------------------------------------------------------------------- +plt.rcParams['font.sans-serif']=['SimHei'] #用来正常显示中文 +plt.rcParams['axes.unicode_minus']=False #用来正常显示负号 +picSize=400 +img_open = None +img_result = None +img_empty = cv2.imread('data/image/OIP-C.jpg',0) + + +# ---------------------------------------------------------------------- +# 几何变化类 +# 水平翻转 +def filp_h(): + global img_open, img_gray, img_result + + if img_open is None: + showwarning(title='警告', message='请先在文件菜单下打开图片!') + return + + transform = A.HorizontalFlip(p=1) + + img_result = transform(image=np.array(img_open)) + img_result = img_result['image'] + placePic2(img_result,'水平翻转后') + +# 垂直翻转 +def flip_v(): + global img_open, img_gray, img_result + + if img_open is None: + showwarning(title='警告', message='请先在文件菜单下打开图片!') + return + + transform = A.VerticalFlip(p=1) + + img_result = transform(image=np.array(img_open)) + img_result = img_result['image'] + + placePic2(img_result,'垂直翻转后') + +# 缩放 +def resize(): + + global img_open, img_gray, img_result + + if img_open is None: + showwarning(title='警告', message='请先在文件菜单下打开图片!') + return + + paraLists = [["水平方向缩放后大小", 224], ["垂直方向缩放后大小", 224]] + paraW = paraWindow(root, "请设置水平和垂直方向的缩放尺寸", paraLists) + + h = int(paraW.paraLists[0][1]) + w = int(paraW.paraLists[1][1]) + + transform = A.Resize(h,w) + img_result = transform(image=np.array(img_open)) + img_result = img_result['image'] + + placePic2(img_result,'缩放后') + +# 旋转(可选择旋转角度) +def roate(): + + global img_open, img_gray, img_result + + if img_open is None: + showwarning(title='警告', message='请先在文件菜单下打开图片!') + return + + paraLists = [['请设置旋转角度',30]] + paraW = paraWindow(root, "旋转图片", paraLists) + + angle = int(paraW.paraLists[0][1]) + + transform = A.Rotate(angle) + img_result = transform(image=np.array(img_open),p=1) + img_result = img_result['image'] + + placePic2(img_result,'旋转后') + +# 裁剪(裁剪方式:centercrop、randomcrop和cutout) +def Centercrop(): + + global img_open, img_gray, img_result + + if img_open is None: + showwarning(title='警告', message='请先在文件菜单下打开图片!') + return + + paraLists = [["水平方向裁剪后大小", 224], ["垂直方向裁剪后大小", 224]] + paraW = paraWindow(root, "请设置水平和垂直方向的裁剪后尺寸", paraLists) + + h = int(paraW.paraLists[0][1]) + w = int(paraW.paraLists[1][1]) + + transform = A.CenterCrop(h, w,p=1) + img_result = transform(image=np.array(img_open)) + img_result = img_result['image'] + + placePic2(img_result,'中心裁剪后') + +def Randomcrop(): + global img_open, img_gray, img_result + + if img_open is None: + showwarning(title='警告', message='请先在文件菜单下打开图片!') + return + + paraLists = [["水平方向缩放后大小", 224], ["垂直方向缩放后大小", 224]] + paraW = paraWindow(root, "请设置水平和垂直方向的缩放尺寸", paraLists) + + h = int(paraW.paraLists[0][1]) + w = int(paraW.paraLists[1][1]) + + transform = A.RandomCrop(h, w,p=1) + img_result = transform(image=np.array(img_open)) + img_result = img_result['image'] + + placePic2(img_result,'随机裁剪后') + +# 反向裁剪(随机选择区域) +def cutout(): + global img_open, img_gray, img_result + + if img_open is None: + showwarning(title='警告', message='请先在文件菜单下打开图片!') + return + + h,w,c = img_open.shape + # 后续是原图和mask相乘,所以初始化为0 + # 不需要裁剪的区域就不变,裁剪的区域变成0 + mask = np.ones_like(img_open) + print(mask) + x = np.random.randint(0,w) + y = np.random.randint(0,h) + + paraLists = [['相对原图裁剪区域的大小', 0.2]] + paraW = paraWindow(root, "请设置具体参数", paraLists) + # p = int(paraW.paraLists[0][1]) + s = float(paraW.paraLists[0][1]) + + w_cut = int(s*w) + h_cut = int(s*h) + + x1 = max(x - w_cut // 2, 0) # 计算裁剪的左上角坐标 + x2 = min(x1 + w_cut, w) # 计算裁剪的右下角坐标 + y1 = max(y - h_cut // 2, 0) + y2 = min(y1 + h_cut, h) + mask[y1:y2, x1:x2, :] = 0 # 将裁剪区域内像素值置为 0 + + + img_result = img_open * mask # 与原始图像按元素相乘,得到 Cutout 后的图像 + print(img_result[y1:y2, x1:x2, :]) + placePic2(img_result,text2='反向随机裁剪后') + +# 对比度、饱和度、亮度、色调变化 +def ColorJitter(): + global img_open, img_gray, img_result + + if img_open is None: + showwarning(title='警告', message='请先在文件菜单下打开图片!') + return + + paraLists = [["亮度", 0], ["色调", 0],['对比度',0],['饱和度',0]] + paraW = paraWindow(root, "请设置具体参数", paraLists) + + + brightness = float(paraW.paraLists[0][1]) # 亮度 + hue = float(paraW.paraLists[1][1]) # 色调 + contrast = float(paraW.paraLists[2][1]) # 对比度 + saturation = float(paraW.paraLists[3][1]) # 饱和度 + + transform = A.ColorJitter(brightness=brightness,hue=hue,saturation=saturation,contrast=contrast,p=1) + img_result = transform(image=np.array(img_open)) + img_result = img_result['image'] + placePic2(img_result) + +# 转换成灰度图 +def ToGray(): + global img_open, img_gray, img_result + + if img_open is None: + showwarning(title='警告', message='请先在文件菜单下打开图片!') + return + + placePic2(img_gray,'灰度图') + +# 通道分离 +def split_channel(): + global img_open, img_gray, img_result + + if img_open is None: + showwarning(title='警告', message='请先在文件菜单下打开图片!') + return + ''' 现在 的img_open 是RGB的''' + R,G,B = cv2.split(img_open) + paraLists = [['分离通道', 0]] + paraW = paraWindow(root, "012分别代表RGB", paraLists) + dict = {'0':R,'1':G,'2':B} + img_result = dict[str(int(paraW.paraLists[0][1]))] + placePic2(img_result,'分离通道后') + +# 随机模糊 +def Blur(): + global img_open, img_gray, img_result + + if img_open is None: + showwarning(title='警告', message='请先在文件菜单下打开图片!') + return + + paraLists = [['模糊的次数',3]] + paraW = paraWindow(root, "请设置具体参数", paraLists) + count = int(paraW.paraLists[0][1]) + transform = A.Blur(blur_limit=count,p=1) + img_result = transform(image=np.array(img_open)) + img_result = img_result['image'] + placePic2(img_result) + +# 高斯滤波 +def Gaussblur(): + global img_open, img_gray, img_result + + if img_open is None: + showwarning(title='警告', message='请先在文件菜单下打开图片!') + return + paraLists = [['模糊的次数', 3]] + paraW = paraWindow(root, "请设置具体参数", paraLists) + count = int(paraW.paraLists[0][1]) + transform = A.GaussianBlur(blur_limit=count, p=1) + img_result = transform(image=np.array(img_open)) + img_result = img_result['image'] + placePic2(img_result,text2='经过高斯模糊后') + +# 中值滤波 +def Medianblur(): + global img_open, img_gray, img_result + + if img_open is None: + showwarning(title='警告', message='请先在文件菜单下打开图片!') + return + paraLists = [['模糊的次数', 3]] + paraW = paraWindow(root, "请设置具体参数", paraLists) + count = int(paraW.paraLists[0][1]) + transform = A.MedianBlur(blur_limit=count, p=1) + img_result = transform(image=np.array(img_open)) + img_result = img_result['image'] + placePic2(img_result,text2='经过中值滤波后') + +# 添加高斯噪声 +def add_gaussian_noise(): + global img_open, img_gray, img_result + + if img_open is None: + showwarning(title='警告', message='请先在文件菜单下打开图片!') + return + + paraLists = [['请输入均值', 0],['请输入标准差',30]] + paraW = paraWindow(root, "请设置具体参数", paraLists) + mean = int(paraW.paraLists[0][1]) + sigma = int(paraW.paraLists[1][1]) + + # 生成高斯噪声矩阵 + row, col, ch = img_open.shape + gaussian = np.random.randn(row, col, ch) * sigma + mean + gaussian = gaussian.reshape(row, col, ch) + img_result = img_open + gaussian + # 转换数据类型为8位无符号整数类型 + img_result = cv2.convertScaleAbs(img_result) + placePic2(img_result,text2='添加高斯噪声后') + +# 添加椒盐噪声 +def add_salt_and_pepper_noise(): + global img_open, img_gray, img_result + + if img_open is None: + showwarning(title='警告', message='请先在文件菜单下打开图片!') + return + + paraLists = [['百分比', 0]] + paraW = paraWindow(root, "请设置具体参数", paraLists) + percentage = int(paraW.paraLists[0][1]) + + # 确保百分比在 0 到 100 之间 + if percentage < 0 or percentage > 100: + showwarning(title='警告', message='百分比必须在 0 到 100 之间!') + return + + # 生成椒盐噪声矩阵 + row, col, ch = img_open.shape + noise = np.zeros((row, col, ch), np.uint8) + for i in range(row): + for j in range(col): + rand = np.random.randint(0, 100) + if rand < percentage: + noise[i][j] = [0, 0, 0] + elif rand > 100 - percentage: + noise[i][j] = [255, 255, 255] + else: + noise[i][j] = img_open[i][j] + + # 将椒盐噪声矩阵添加到原始图像中 + img_result = cv2.add(img_open, noise) + + placePic2(img_result,text2='添加椒盐噪声后') + +# 添加均值噪声 +def add_mean_noise(): + global img_open, img_gray, img_result + + if img_open is None: + showwarning(title='警告', message='请先在文件菜单下打开图片!') + return + + paraLists = [['均值', 0], ['标准差', 30]] + paraW = paraWindow(root, "请设置具体参数", paraLists) + mean = int(paraW.paraLists[0][1]) + std_dev = int(paraW.paraLists[1][1]) + + # 生成均值噪声矩阵 + row, col, ch = img_open.shape + noise = np.random.normal(mean, std_dev, (row, col, ch)).astype(np.uint8) + + # 将均值噪声矩阵添加到原始图像中 + img_result = cv2.add(img_open, noise) + + placePic2(img_result,text2='添加均值噪声后') + +# 使用sobel算子进行锐化 +def sobel(): + global img_open, img_gray, img_result + + if img_open is None: + showwarning(title='警告', message='请先在文件菜单下打开图片!') + return + + kernelx1 = np.array([[-1,-2,-1],[0,0,0],[1,2,1]], dtype=int) + kernely1 = np.array([[-1,0,1],[-2,0,2],[-1,0,1]], dtype=int) + x1 = cv2.filter2D(img_gray, cv2.CV_16S, kernelx1) + y1 = cv2.filter2D(img_gray, cv2.CV_16S, kernely1) + absX1 = cv2.convertScaleAbs(x1) + absY1 = cv2.convertScaleAbs(y1) + img_result = cv2.addWeighted(absX1, 0.5, absY1, 0.5, 0) + placePic2(img_result) + +# 使用Prewitt算子进行锐化 +def Prewitt(): + global img_open, img_gray, img_result + + if img_open is None: + showwarning(title='警告', message='请先在文件菜单下打开图片!') + return + + kernelx1 = np.array([[-1,0,1],[-1,0,1],[-1,0,1]], dtype=int) + kernely1 = np.array([[-1,-1,1],[0,0,0],[1,1,1]], dtype=int) + x1 = cv2.filter2D(img_gray, cv2.CV_16S, kernelx1) + y1 = cv2.filter2D(img_gray, cv2.CV_16S, kernely1) + absX1 = cv2.convertScaleAbs(x1) + absY1 = cv2.convertScaleAbs(y1) + img_result = cv2.addWeighted(absX1, 0.5, absY1, 0.5, 0) + placePic2(img_result) + +# 使用robert算子进行锐化 +def robert(): + global img_open, img_gray, img_result + + if img_open is None: + showwarning(title='警告', message='请先在文件菜单下打开图片!') + return + + kernelx1 = np.array([[-1, 0], [0, 1]], dtype=int) + kernely1 = np.array([[0, -1], [1, 0]], dtype=int) + x1 = cv2.filter2D(img_gray, cv2.CV_16S, kernelx1) + y1 = cv2.filter2D(img_gray, cv2.CV_16S, kernely1) + absX1 = cv2.convertScaleAbs(x1) + absY1 = cv2.convertScaleAbs(y1) + img_result = cv2.addWeighted(absX1, 0.5, absY1, 0.5, 0) + placePic2(img_result) + +# 直方图均衡化 +def equalhist(): + global img_open, img_gray, img_result + + if img_open is None: + showwarning(title='警告', message='请先在文件菜单下打开图片!') + return + + # 说明是灰度图像 + if len(img_open)==2: + img_result = cv2.equalizeHist(img_open) + # RGB图像分别对每个通道都均衡化 然后在 合并 + else: + b,g,r = cv2.split(img_open) + b_eq = cv2.equalizeHist(b) + g_eq = cv2.equalizeHist(g) + r_eq = cv2.equalizeHist(r) + + img_result = cv2.merge([b_eq,g_eq,r_eq]) + placePic2(img_result) + +# 叠加图片操作 +def mixup(): + global img_open, img_gray, img_result + + if img_open is None: + showwarning(title='警告', message='请先在文件菜单下打开图片!') + return + + img_show1 = img_open + # 读取另一张图片 + path = askopenfilename(title='打开另一张图片') + + if path!=0 and path!='': + img_show2 = cv_imread(path) + + if img_show2 is not None: + placePic2(img_show2) + else: + placePic2(img_empty) + showwarning(title='警告',message='请打开第二张图片') + + # 下面进行mixup操作 + paraLists = [['请输入一张图片所占比列', 0.5]] + paraW = paraWindow(root, "请设置具体参数", paraLists) + + # 判断两张图片的shape 是否相同 不相同则都取小的那边 + if img_show1.shape[:-1]!=img_show2.shape[:-1]: + showwarning(title='警告',message='两张图片大小不一样,会自动做裁剪使大小相同') + if img_show1.shape[0] > img_show2.shape[0]: + h = img_show2.shape[0] + else: + h = img_show1.shape[0] + + if img_show1.shape[1] > img_show2.shape[1]: + w = img_show2.shape[1] + else: + w = img_show1.shape[1] + img_show1 = cv2.resize(img_show1,(h,w)) + img_show2 = cv2.resize(img_show2,(h,w)) + + # 判断图片的channel是否相同,如果不相同 在全部转换为 RGB的 + if len(img_show1)==2: + img_show1 = cv2.cvtColor(img_show1,cv2.COLOR_GRAY2RGB) + if len(img_show2)==2: + img_show2 = cv2.cvtColor(img_show1,cv2.COLOR_GRAY2RGB) + alpha = float(paraW.paraLists[0][1]) + print(alpha) + # 这个方法需要两张图片的大小相同 需要做个resize操作 + img_result = cv2.addWeighted(img_show1,alpha,img_show2,1-alpha,0) + cv_show('after mixup',img_result) + + +# ---------------------------------------------------------------------- +def cv_show(name,img): + + # 打开文件后会有img_open,img_gray的信息,img_result指进行操作后的可视化结果 + # 图片很大时需要进行resize + h,w = img.shape[:2] + if h>1000 or w>1000: + img = cv2.resize(img,dsize=None,fx=0.8,fy=0.8) + + cv2.imshow(name,img) + cv2.waitKey(0) + cv2.destroyAllWindows() + +def cv_imread(filename,colorMode=cv2.IMREAD_COLOR): + + # 读取文件路径中带汉字的图片,替代cv2.imread(filename) + # :param filename: 需要读取的文件 + # :param colorMode: 彩色模式 + # :return: 读取的图片 + + return cv2.imdecode(np.fromfile(filename, dtype=np.uint8), colorMode) + +def cv_imwrite(path, img): + + # 解决路径中有汉字的图片保存问题 + # :param path: 保存的路径 + # :param img: 保存的图片 + + cv2.imencode('.jpg', img)[1].tofile(path) + +def placePic1(img_show, text1="原始图像"): + + # 将打开的初始图像放置在左边窗格 + # :param img_show: 需要显示的图片 + # :param text1: 图片说明 + + global picSize + if img_show is None: + showwarning(title='警告', message='未打开图片!') + return + img = img_show.copy() + if len(img.shape) > 2: + img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB) + height, width = img.shape[:2] + scaling = max(width, height) / picSize + newH = int(height / scaling) + newW = int(width / scaling) + img0 = Image.fromarray(img) # 由OpenCV图片转换为PIL图片格式 + img0 = img0.resize((newW, newH)) + img0 = ImageTk.PhotoImage(img0) + myWindow.originalText.set(text1) + myWindow.label3.config(image=img0) + myWindow.label3.image = img0 + myWindow.label3.place(relx=0.25, rely=0.40, width=picSize, height=picSize, anchor=tkinter.CENTER) # 设置绝对座标 + +def placePic2(img_show, text2="处理结果图", RGB=True): + + # 将处理结果图像放置在右边窗格 + # :param img_show: 需要显示的图像 + # :param text2: 图像文字说明 + # :param RGB: 是否转换成RGB,默认转换 + + global picSize, img_result + if img_show is None: + showwarning(title='警告', message='没有结果图片!') + return + img = img_show.copy() + if len(img.shape) > 2 and RGB: + img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB) + height, width = img.shape[:2] + # img = PointProcessing.global_linear_transmation(img) + scaling = max(width, height) / picSize + newH = int(height / scaling) + newW = int(width / scaling) + img0 = Image.fromarray(img) # 由OpenCV图片转换为PIL图片格式 + img0 = img0.resize((newW, newH)) + img0 = ImageTk.PhotoImage(img0) + myWindow.resultText.set(text2) + myWindow.label4.config(image=img0) + myWindow.label4.image = img0 # 通过标签来显示图片,使用ImageTK把img转成tk可以接收的形式 + myWindow.label4.place(relx=0.75, rely=0.40, width=picSize, height=picSize, anchor=tkinter.CENTER) + +def Choosepic(): + # 打开文件选择对话框,并读取图片文件,存放在img_open全局变量中 + global picSize, path_, img_open, img_gray, img_empty + path_ = askopenfilename(title="打开需要处理的图片") + # path.set(path_) + if path_ != 0 and path_ != '': + img_open = cv_imread(path_) # 读取文件路径中有汉字的图片文件,读取的是RGB图像 + img_gray = cv2.cvtColor(img_open,cv2.COLOR_BGR2GRAY) + if img_open is not None: + placePic1(img_open) + else: + placePic1(img_empty) + showwarning(title='警告', message='无图片!') + else: + img_open = None + img_gray = None + placePic1(img_empty) + showwarning(title='警告', message='未选择图片!') + +def Savepic(): + # 打开文件对话框,将img_result图片保存到指定文件 + global img_result + if img_result is not None: + fname = asksaveasfilename(title='保存文件', defaultextension='default.jpg',filetypes=[("JPG", ".jpg")]) + if fname: + cv_imwrite(str(fname), img_result) + showinfo(title='提示', message='图片已保存!') + else: + showwarning(title='警告', message='请先输入文件名!') + else: + showwarning(title='警告', message='请先处理图片!') + +def initWindows(): + # 将系统界面恢复到初始界面,并不清除结果图片 + myWindow.setVisibleLeft() + myWindow.setVisibleRight0() + myWindow.hideFig0() + myWindow.hideFig1() + + if img_result is not None: + placePic2(img_result) + +class ImageApp: + global picSize + + def __init__(self, master): + self.master = master + self.master.title("图像多功能增强可视化窗口") + + # 界面布局 + self.init_menu(self.master) # 初始化菜单 + self.init_widget(self.master) # 初始化需要用的控件(两个画布) + self.setVisibleLeft() # 显示左窗格 + self.setVisibleRight0() # 显示右窗格 + + self.hideFig0() # 隐藏左窗格的画布myCanvas0 + self.hideFig1() # 隐藏右窗格的画布myCanvas1 + + def init_widget(self, master): + self.originalText = tkinter.StringVar() + self.label1 = tkinter.Label(master, textvariable=self.originalText, font=("song", 12)) + self.originalText.set("原始图像") + + self.label3 = tkinter.Label(master, bg='gray86') # 用于显示原始图像,背景颜色设置成灰色 + + self.resultText = tkinter.StringVar() + self.label2 = tkinter.Label(master, textvariable=self.resultText, font=("song", 12)) + self.resultText.set("处理结果图") + + self.label4 = tkinter.Label(master, bg='gray86') # 用于显示处理结果,背景颜色设置成灰色 + + self.explainText = tkinter.StringVar() + + self.myFig0 = plt.figure(figsize=(4, 4), facecolor='#dddddd') + self.myCanvas0 = FigureCanvasTkAgg(self.myFig0, master) + # myfig1在下边窗格的画布中显示多张包含图片的注释说明 + self.myFig1 = plt.figure(figsize=(9, 1.8), facecolor='lightgray') + # myfig2在下边窗格中显示说明与单张图片 + self.myFig2 = plt.figure(figsize=(4.5, 1.8), facecolor='lightgray') + self.myCanvas1 = FigureCanvasTkAgg(self.myFig1, master) + self.myCanvas2 = FigureCanvasTkAgg(self.myFig2, master) + + def init_menu(self,master): + + # 创建菜单和其内容 + menubar = tk.Menu(master) # 创建菜单对象 + + # 创建菜单内容 后面加command 可以创建子菜单 到时候看看需不需要 + fmenu_file = tk.Menu(master) # 文件 + fmenu_file.add_command(label='打开',command=Choosepic) + fmenu_file.add_command(label='保存处理结果', command=Savepic) + fmenu_file.add_separator() # 添加一条分隔线 + fmenu_file.add_command(label='退出',command=quit) + + # 几何变换 + fmenu_geo = tk.Menu(master) + fmenu_geo.add_command(label='水平翻转', command=filp_h) + fmenu_geo.add_command(label='垂直翻转', command=flip_v) + fmenu_geo.add_command(label='旋转', command=roate) + fmenu_geo.add_command(label='缩放', command=resize) + + # 给裁剪菜单再次创建子菜单 + submenu_crop = tk.Menu(fmenu_geo) + fmenu_geo.add_cascade(label='裁剪',menu=submenu_crop) + submenu_crop.add_command(label='中心裁剪',command=Centercrop) + submenu_crop.add_command(label='随机裁剪', command=Randomcrop) + submenu_crop.add_command(label='反向随机裁剪', command=cutout) + + # 颜色空间变换 + fmenu_sp = tk.Menu(master) + fmenu_sp.add_command(label='对比度变换',command=ColorJitter) + fmenu_sp.add_command(label='亮度变换', command=ColorJitter) + fmenu_sp.add_command(label='饱和度变换', command=ColorJitter) + fmenu_sp.add_command(label='色调',command=ColorJitter) + fmenu_sp.add_command(label='通道分离', command=split_channel) + fmenu_sp.add_command(label='灰度图', command=ToGray) + fmenu_sp.add_command(label='直方图均衡化', command=equalhist) + + # 像素点操作 + fmenu_pix = tk.Menu(master) # 创建像素点操作的主菜单 + submenu_blur = tk.Menu(fmenu_pix) # 模糊子菜单 + fmenu_pix.add_cascade(label='模糊',menu=submenu_blur) # 把模糊子菜单加到模糊下面 + # 给模糊子菜单加子菜单 + submenu_blur.add_command(label='高斯滤波',command=Gaussblur) + submenu_blur.add_command(label='随机模糊', command=Blur) + submenu_blur.add_command(label='中值滤波', command=Medianblur) + + submenu_sh = tk.Menu(fmenu_pix) + fmenu_pix.add_cascade(label='锐化',menu=submenu_sh) + # 给锐化加子菜单 + submenu_sh.add_command(label='robort算子',command=robert) + submenu_sh.add_command(label='Prewitt算子',command=Prewitt) + submenu_sh.add_command(label='Sobel算子',command=sobel) + + submenu_noise = tk.Menu(fmenu_pix) + fmenu_pix.add_cascade(label='添加噪声',menu=submenu_noise) + # 给添加噪声加子菜单 + submenu_noise.add_command(label='高斯噪声',command=add_gaussian_noise) + submenu_noise.add_command(label='椒盐噪声',command=add_salt_and_pepper_noise) + submenu_noise.add_command(label='均值噪声',command=add_mean_noise) + + # 基于多张图片 + fmenu_mul = tk.Menu(master) + fmenu_mul.add_command(label='mixup',command=mixup) + + # 将菜单内容 添加到menubar + menubar.add_cascade(label="文件", menu=fmenu_file) + menubar.add_cascade(label="几何变化", menu=fmenu_geo) + menubar.add_cascade(label="颜色空间变换", menu=fmenu_sp) + menubar.add_cascade(label="频域像素点操作", menu=fmenu_pix) + menubar.add_cascade(label='基于多张图片',menu=fmenu_mul) + + # 整体显示 + master.config(menu=menubar) + + def setVisibleLeft(self): + # label1是文字,label3目前是空只是占了位置,且背景颜色是灰色,需要放图片 + + self.label1.place(relx=0.25, rely=0.04, anchor=tkinter.CENTER) # 设置相对座标 + self.label3.place(relx=0.25, rely=0.40, width=picSize, height=picSize,anchor=tkinter.CENTER) # 设置绝对座标 + + def setVisibleRight0(self): + # 右边窗格重叠的显示之一(right0) + # right0一般算法结果显示窗格 + # right1-用于显示车牌识别结果 + # myFig0-使用mayplot显示运行结果图片 + # label2 是文字,label4目前是空只是占了位置 且背景颜色是灰色,需要放图片 + + self.label2.place(relx=0.75, rely=0.04, anchor=tkinter.CENTER) + self.label4.place(relx=0.75, rely=0.40, width=picSize, height=picSize,anchor=tkinter.CENTER) + + def setVisibleBottom(self): + + # 下边窗格的重叠显示之一(label5) + # label5-仅显示文字说明 + # myFig1-用matplot显示多个文字图片 + # explain-左边是label5,右边是myFig2 + + self.label5.place(relx=0.50, rely=0.86, width=900, height=180,anchor=tkinter.CENTER) + self.hideFig1() + self.hideExplain() + + def hideFig0(self): + self.myCanvas0.get_tk_widget().place_forget() # 隐藏画布self + + def hideFig1(self): + self.myCanvas1.get_tk_widget().place_forget() # 隐藏画布self + + def hideExplain(self): + self.label5.config(wraplength=850) + self.myCanvas2.get_tk_widget().place_forget() # 隐藏画布self(暂时隐藏,用的时候可以用place方法恢复) + + # 有的功能还需要选择具体参数,这个时候需要弹出一个小窗口给参数,并获取这些参数值,给具体的变化 + + +# ---------------------------------------------------------------------- +class paraWindow(tkinter.Toplevel): + + # 多行一列的参数设置窗口 + def __init__(self, root, title = None, paraLists=[]): + # Constructor + self.root = root + self.paraLists=paraLists + self.names = locals() #动态组件 + tkinter.Toplevel.__init__(self,root) + if title: + self.title(title) + # 创建对话框的主体内容 + frame = tkinter.Frame(self) + # 调用init_widgets方法来初始化对话框界面 + self.initial_focus = self.init_widgets(frame) + frame.pack(padx=5, pady=5) + # 根据modal选项设置是否为模式对话框 + self.grab_set() #重要,必须是模式对话框 + # 为"WM_DELETE_WINDOW"协议使用self.cancel_click事件处理方法 + self.protocol("WM_DELETE_WINDOW", self.cancel_click) + # 根据父窗口来设置对话框的位置 + self.geometry("+%d+%d" % (root.winfo_rootx() + 100, root.winfo_rooty() + 100)) + # 让对话框获取焦点 + self.initial_focus.focus_set() + self.wait_window(self) + + def init_widgets(self,master): + # 创建自定义对话框的内容 + nrow=0 + for i in range(len(self.paraLists)): + str0 = self.paraLists[i][0] + nrow = i + 1 + labelMessage = tkinter.Label(master, text=str0, font=("song", 12)) + labelMessage.grid(row=nrow, column=0) + self.names['paraV' + str(i)] = tkinter.StringVar() + self.names['paraE' + str(i)] = tkinter.Entry(master, textvariable=self.names['paraV' + str(i)], width=20) + self.names['paraV' + str(i)].set(self.paraLists[i][1]) + self.names['paraE' + str(i)].grid(row=nrow, column=1) # 控件按列排列 + b1 = tkinter.Button(master, text='确定退出', command=self.setPara) + b1.grid(row=nrow + 1, column=1) + self.bind("", self.setPara) + self.bind("", self.cancel_click) + return self.names['paraE0'] + + def cancel_click(self, event=None): + showwarning(title='警告', message='必须先设置参数') + self.initial_focus.focus_set() + + def setPara(self): + # 通过对话框设置参数 + for i in range(len(self.paraLists)): + text0 = self.names['paraV' + str(i)].get() + if not self.on_validate(text0): # 如果不能通过校验,让用户重新输入 + showwarning(title='警告', message='必须输入数字') + self.names['paraV' + str(i)].set(self.paraLists[i][1]) + self.names['paraE'+ str(i)].focus_set() + return + else: + self.paraLists[i][1] = float(text0) + self.hide() + + def on_validate(self,content): + # 该方法可对用户输入的数据进行校验,保证输入的是数字 + for i in range(len(content) - 1, -1, -1): + if not (48 <= ord(content[i]) <= 57 or content[i] == "." or content[i] =="+" or content[i] =="-" ): + return False + return True + + def hide(self): + # 销毁对话框 + self.withdraw() + self.update_idletasks() + # 将焦点返回给父窗口 + self.root.focus_set() + # 销毁自己 + self.destroy() + self.root.update() + self.root.deiconify() + self.root.focus_set() + +class paraWindow2(tkinter.Toplevel): + # 多行多列参数设置窗口 + def __init__(self, root, paraLists,title,explain): + # Constructor + self.root = root + self.names=locals() + self.paraLists=paraLists + self.explain=explain + tkinter.Toplevel.__init__(self,root) + # self.geometry("400x300") + self.title(title) + # 调用init_widgets方法来初始化对话框界面 + self.initial_focus = self.init_widgets() + # 根据modal选项设置是否为模式对话框 + self.grab_set() + # 为"WM_DELETE_WINDOW"协议使用self.cancel_click事件处理方法 + self.protocol("WM_DELETE_WINDOW", self.cancel_click) + # 根据父窗口来设置对话框的位置 + self.geometry("+%d+%d" % (root.winfo_rootx() + 100, root.winfo_rooty() + 100)) + # print(self.initial_focus) + # 让对话框获取焦点 + self.initial_focus.focus_set() + self.wait_window(self) + + def init_widgets(self): + frame1 = tkinter.Frame(self) + explainLabel = tkinter.Label(frame1, text=self.explain, font=("song", 12)) + explainLabel.pack(padx=50, pady=5) + frame1.pack(padx=5, pady=5) + frame2 = tkinter.Frame(self) + nrow=1 + for i in range(len(self.paraLists)): + nrow = i + 3 + for j in range(len(self.paraLists[i])): + self.names['ev' + str(i) + str(j)] = tkinter.StringVar() + self.names['e' + str(i) + str(j)] = tkinter.Entry(frame2, textvariable=self.names['ev' + str(i) + str(j)],width=10) + self.names['ev' + str(i) + str(j)].set(self.paraLists[i][j]) + self.names['e' + str(i) + str(j)].grid(row=nrow, column=j) + b1 = tkinter.Button(frame2, text='确定退出', command=self.setPara) + b1.grid(row=nrow + 1, column=1) + frame2.pack(padx=5, pady=6) + return b1 + + def setPara(self): + # 通过对话框给参数赋值 + for i in range(len(self.paraLists)): + for j in range(len(self.paraLists[i])): + text0 = self.names['ev' + str(i) + str(j)].get() + if not self.on_validate(text0): # 如果不能通过校验,让用户重新输入 + showwarning(title='警告', message='必须输入数字') + self.names['ev' + str(i) + str(j)].set(self.paraLists[i][j]) + self.names['e' + str(i) + str(j)].focus_set() + return + else: + self.paraLists[i][j] = float(text0) + self.hide() + + def on_validate(self,content): + # 该方法可对用户输入的数据进行校验,保证输入的是数字 + for i in range(len(content) - 1, -1, -1): + if not (48 <= ord(content[i]) <= 57 or content[i] == "." or content[i] == "+" or content[i] == "-"): + return False + return True + + def cancel_click(self, event=None): + showwarning(title='警告', message='必须先设置参数') + self.initial_focus.focus_set() + + def hide(self): + self.withdraw() + self.update_idletasks() + # 将焦点返回给父窗口 + self.root.focus_set() + # 销毁自己 + self.destroy() + self.root.update() + self.root.deiconify() + self.root.focus_set() + +class listWindow(tkinter.Toplevel): + # 下拉列表窗口,用对参数设置 + def __init__(self, root, title = None, paraLists=[]): + # Constructor + self.root = root + self.paraLists=paraLists + tkinter.Toplevel.__init__(self,root) + if title: + self.title(title) + # 创建对话框的主体内容 + # 调用init_widgets方法来初始化对话框界面 + self.initial_focus = self.init_widgets(self) + # 根据modal选项设置是否为模式对话框 + self.grab_set() + # 为"WM_DELETE_WINDOW"协议使用self.cancel_click事件处理方法 + self.protocol("WM_DELETE_WINDOW", self.cancel_click) + # 根据父窗口来设置对话框的位置 + self.geometry("+%d+%d" % (root.winfo_rootx() + 100, root.winfo_rooty() + 100)) + # print(self.initial_focus) + # 让对话框获取焦点 + self.initial_focus.focus_set() + self.wait_window(self) + + def init_widgets(self,master): + str0 = self.paraLists[0][0] + labelMessage = tkinter.Label(master, text=self.paraLists[0][0], font=("song", 12)) + labelMessage.grid(row=0, column=0) + self.listBox = tkinter.Listbox(master,selectmode=tkinter.SINGLE) + self.listBox.grid(row=1, column=0) + lists=self.paraLists[0][1] + for item in lists: + self.listBox.insert(tkinter.END, item) + self.listBox.select_set(0) + b1 = tkinter.Button(master, text='确定退出', command=self.setPara) + b1.grid(row=2, column=0) + self.bind("", self.setPara) + self.bind("", self.cancel_click) + return self.listBox + + def cancel_click(self, event=None): + showwarning(title='警告', message='必须先设置参数') + self.initial_focus.focus_set() + + def setPara(self): + self.returnValue=self.listBox.curselection()[0] + self.hide() + + def hide(self): + self.withdraw() + self.update_idletasks() + # 将焦点返回给父窗口 + self.root.focus_set() + # 销毁自己 + self.destroy() + self.root.update() + self.root.deiconify() + self.root.focus_set() + + +# ---------------------------------------------------------------------- +if __name__ == "__main__": + root = tk.Tk() + root.geometry("1000x640+150+5") # 界面大小,以相对屏幕的坐标 + root.resizable(0, 0) + myWindow = ImageApp(root) + root.mainloop() \ No newline at end of file