使用Python和OpenCV创建tkinter交互式图像处理应用程序

tkinter 基本控件与使用

我们将学习如何使用Tkinter包编写一些图形用户界面程序。Tkinter是Python的一个标准包,因此我们并不需要安装它。我们将从创建一个窗口开始,然后我们在其之上加入一些小组件,比如按钮,复选框等,并使用它们的一些属性。话不多说,让我们开始吧!
from tkinter import *

使用tkinter实现交互式图像处理

  • tkinter基本控件
  • Button按钮控件
  • canvas
  • Label
  • 补充知识
  • 布局管理器grid
  • 使用tkinter+opencv 制作交互式程序
  • 小试牛刀
  • 利用滑块旋转图片,提供保存按钮
  • 一个实现好的作品
  • tkinter基本控件

    Button按钮控件

    普通的按钮是非常简单易用的。你所需要做的就是指定 Button 的内容(文本、位图或者图片),并且关联当按钮被按下时应该调用的函数或方法:

    def callback():
    	print("你点击了按钮")
    
    window=Tk()#创建一个窗口
    buttion1=Button(window, text='press', command=callback)#如果command还没有实现好功能,可以先填入'disabled'
    buttion1.pack()#打包组件
    
    window.mainloop()#维持窗口直至手动关闭
    


    你得到了一个小家伙,但后面我们会遇到棘手的大家伙的!

    实现了简单的按钮之后,你可能会不满足于此。的确,每个人都希望他的window可以大小适中,按钮也符合规律的排布。下面的代码实现了你渴望的功能——

    def callback():
        print("点赞,关注,投币,评论,转发")
    win = Tk()
    win.title('一个大小合适的窗口')
    win.geometry('800x600')  # size
    win['bg'] = 'pink'  # background color
    win.attributes('-alpha', 0.9)  # 虚化
    button = Button(win,text='这是一个按钮',
                    padx=100,pady=100,command=callback)
    #,height=2,width=50,command=callback)
    button.grid(row=1, column=11)
    win.mainloop()
    

    canvas

    canvas画布为tkinter的图像绘制提供了基础。canvas是一个高度灵活的组件,可以用来绘制图形和图表,创建图形编辑器,实现各种自定义小部件。
    我们话不多说,演示如下代码查看结果:

    win=Tk()
    win.geometry('800x600')
    w=Canvas(win,width=100,height=300)
    w.pack()
    w.create_line(0, 50, 200, 50, fill = "yellow")
    #画一条红色的竖线(虚线)
    w.create_line(100, 0, 100, 100, fill = "red", dash = (4, 4))
    #中间画一个蓝色的矩形
    w.create_rectangle(50, 25, 150, 75, fill = "blue")
    win.mainloop()
    

    Label

    相信聪明的小伙伴应该知道下面的代码怎么用啦

    Label(标签)组件用于在屏幕上显示文本或图像。Label 组件仅能显示单一字体的文本,但文本可以跨越多行。另外,还可以为其中的个别字符加上下划线(例如用于表示键盘快捷键)。

    一个基础示例

    w=Label(win,text='这只是这个label的文本').pack()
    

    一个换行实例

    longtext = """
    Label 可以显示多行文本,你可以直接使用换行符
    或使用 wraplength 选项来实现。当文本换行的时
    候,你可以使用 anchor 和 justify 选项来使得
    文本如你所希望的显示出来。
    """
    w = Label(master, text=longtext, anchor="w", justify="left")
    

    一个展示图片实例
    下面这个程序需要你输入图片地址(不可以是jpg)

    win=Tk()
    #from PIL import ImageTk
    #img=Image.open('pics/1.jpg')
    #photo=ImageTk.PhotoImage(img)
    #a=Label(win,image=photo).pack()
    photo = PhotoImage(file="python.gif")
    w = Label(win, image=photo)
    w.pack()
    win.mainloop()
    

    看了上面的代码,聪明的同学已经知道怎么使得输入变成jpg了
    emm可能你比较懒,ok我们这里还有一份可以运行的不用修改的最最最关键的是对jpg文件有效的
    聪明的小伙伴已经在修改窗口大小以验证下面某行代码的必要性啦

    win=Tk()
    img=Image.open('pics/1.jpg')
    size=img.size
    win.geometry('{}x{}'.format(size[0]+100,size[1]+100))#没错就是我,我就是这行
    photo=ImageTk.PhotoImage(img)
    a=Label(win,image=photo).pack()
    win.mainloop()
    

    好啦,你已经学了这么多知识了,是时候再再再补充一点知识啦——

    补充知识

    这一部分我会慢慢完善的

    布局管理器grid

    grid 管理器可以说是 Tkinter 这三个布局管理器中最灵活多变的。如果你只希望学习使用一个布局管理器,那么 grid 绝对是首选。当你在设计对话框的时候,使用 gird 尤其便捷。如果你此前一直在用 pack 构造窗口布局,那么学习完 grid 你会悔恨当初为啥不早学它。使用一个 grid 就可以简单的实现你用很多个框架和 pack 搭建起来的效果。

    细心的读者可能已经发现了,在button控件中,我们使用了两种不同的布局方式:pack, grid

    使用 grid 排列组件,只需告诉它你想要将组件放置的位置(行/列,row 选项指定行,cloumn 选项指定列)。此外,你并不用提前指出网格(grid 分布给组件的位置称为网格)的尺寸,因为管理器会自动计算。

    root = Tk()
     
    # column 默认值是 0
    tk.Label(root, text="用户名").grid(row=0)
    tk.Label(root, text="密码").grid(row=1)
    #tk.Label(root, text="用户名").grid(row=0, sticky="w") #细心的读者可以用下面的代码块代替上面两行
    #tk.Label(root, text="密码").grid(row=1, sticky="w")
    tk.Entry(root).grid(row=0, column=1)
    tk.Entry(root, show="*").grid(row=1, column=1)
    #photo = tk.PhotoImage(file="logo.gif")
    #tk.Label(root, image=photo).grid(row=0, column=2, rowspan=2, padx=5, pady=5) #rowspan=2 跨两行
    tk.Button(text="提交", width=10).grid(row=2, columnspan=3, pady=5) #columnspan=3 跨三列
    root.mainloop()
    

    使用tkinter+opencv 制作交互式程序

    小试牛刀

    ok,现在你真的学了很多知识了,让我们开始第一部:
    制作一个界面,使得用户可以打开指定图片并展示

    阿这,你们肯定想,这么简单的东西还用我教么!
    好的,那我们直接下一个部分——

    不过~
    写代码切忌眼高手低啊!

    win=Tk()
    win.geometry('100x80')
    global message
    message=0
    def getpathandshow():
        global message
        global a
        message=input('please input a pic path')
        print(message)
        if len(message)>0:
    #         assert os.path.exists(message), '路径不存在'
            img=Image.open(message)
            size=img.size
            win.geometry('{}x{}'.format(size[0]+100,size[1]+100))
            photo=ImageTk.PhotoImage(img)
            if a==None:
                a=Label(win,image=photo)
                a.image=photo
                a.pack()
            else:
                a.configure(image=photo)
                a.image=photo
    a=None
    b=Button(win,text='点击输入图片路径',command=getpathandshow).pack()
    win.mainloop()
    

    为了更好的选择图片,我们使用了filedialog包
    获取path修改为:
    path=filedialog.askopenfilename()
    ok,相信你应该明白该把这句话修改在何处,聪明的读者。

    下面我们更加仔细,更加美观地实现了上面所述功能

    from tkinter import *
    import time
    import os
    from PIL import Image, ImageTk
    import cv2 as cv
    from tkinter import filedialog
    
    
    def button2():
        global lab1
        path = entry1.get()
        print(path)
        if len(path) > 0:
            img = Image.open(path)
            size = img.size
            if size[0] > 800 or size[1] > 600:
                rate = max(size[0] / 800, size[1] / 600)
                rsize = (int(size[0] / rate), int(size[1] / rate))
                img = img.resize(rsize)
            # win.geometry('{}x{}'.format(size[0] + 100, size[1] + 100))
            photo = ImageTk.PhotoImage(img)
            if lab1 == None:
                lab1 = Label(win, image=photo)
                lab1.image = photo
                lab1.grid(row=3, columnspan=5)
            else:
                lab1.configure(image=photo)
                lab1.image = photo
    
    
    def button1():
        global lab1
        # message='pics/1.jpg'#input('please input a pic path')
        path = filedialog.askopenfilename()
        print(path)
        if len(path) > 0:
            img = Image.open(path)
            size = img.size
            if size[0] > 800 or size[1] > 600:
                rate = max(size[0] / 800, size[1] / 600)
                rsize = (int(size[0] / rate), int(size[1] / rate))
                img = img.resize(rsize)
            # win.geometry('{}x{}'.format(size[0] + 100, size[1] + 100))
            photo = ImageTk.PhotoImage(img)
            if lab1 == None:
                lab1 = Label(win, image=photo)
                lab1.image = photo
                lab1.grid(row=3, columnspan=5)
            else:
                lab1.configure(image=photo)
                lab1.image = photo
    
    
    win = Tk()
    win.geometry('800x650')
    global lab1
    lab1 = None
    
    entry1 = Entry(win, width=20)
    entry1.grid(row=0, column=1)
    lab2 = Label(win, text='输入图片路径', width=20).grid(row=0, column=0)
    lab2 = Label(win, text='手动选择路劲', width=20).grid(row=0, column=3)
    b1 = Button(win, text='选择路径', command=button1, width=20).grid(row=0, column=4)
    b2 = Button(win, text='确认路径', command=button2, width=20).grid(row=0, column=2)
    
    win.mainloop()
    
    

    利用滑块旋转图片,提供保存按钮

    ok,相信你已经学到了很多,并且跃跃欲试——
    现在!开始尝试并实现一下功能:

    #打开,保存,退出,转灰度
    #几何变换:镜像,平移,剪切,旋转,缩放

    下面的这段代码实现了打开指定路径下的图片,并实现旋转操作
    (其实是我写好了怕以后出bug无处参考哈哈)

    #测试旋转框函数的可行性
    
    #     l1.configure(image=img1)
    a=tk.Tk()
    global path
    img=Image.open(path)
    img=ImageTk.PhotoImage(img)
    #这次我们换一个思路,先加最关键的控件
    
    #最终画布
    l1=tk.Label(a,image=img)
    l1.grid(row=3,columnspan=2)
    
    #角度
    agl=tk.IntVar()
    agl.set(0)
    
    #定义setangle函数,为滑块提供反馈
    def setangle(value):
        agl.set(value)
        
    #滑块
    scale24=tk.Scale(a,from_=0,to=360,orient=tk.HORIZONTAL,showvalue=0,tickinterval=45,
                    resolution=1,command=setangle)
    scale24.grid(row=2,columnspan=2,stick=tk.EW)
    
    #按钮函数组件1:旋转图像函数
    def rotimg():
        #angle.path
        img=cv.imread(path)
        angle=agl.get()
        (h,w)=img.shape[:2]
        center=(w//2,h//2)
        M=cv.getRotationMatrix2D(center,angle,1.0)
        rotedimg=cv.warpAffine(img,M,(w,h))
        rgb_img=cv.cvtColor(rotedimg,cv.COLOR_BGR2RGB)
        img=Image.fromarray(rgb_img)
        return img
    
    #利用组件函数,构建按钮函数
    def button24():
        img=rotimg()
        #img.show()
        img=ImageTk.PhotoImage(img)
        l1.configure(image=img)
        a.mainloop()
    
    #按钮控件
    b1=tk.Button(a,width=20,command=button24,text='显示旋转图')
    b1.grid(row=1,column=1,stick=tk.EW)
    
    #角度显示盘
    la=tk.Label(a,textvariable=str(agl),bg='pink')
    la.grid(row=1,column=0,stick=tk.EW)
    
    #好的,既然我们实现了困难的部分,下面开始实现简单的部分,保存路径输入框entry
    entry01=tk.Entry(a)
    entry01.grid(row=0,column=0,stick=tk.EW)
    
    #下面的部分是保存图片的按钮,会利用到之前的rotimg函数,和之前一样,先写按钮函数
    def button02():
        path=entry01.get()
        if len(path) == 0:
            print('保存失败')
            return 0
        img=rotimg()
        img.save(path)
        #保存成功我们希望有个弹窗
        winmessage=tk.Toplevel(a)
        winmessage.geometry('300x200')
        tk.Message(winmessage,text='恭喜你,或者说恭喜这个熬了很久的猿,你们保存成功了').grid(sticky=tk.NSEW)
        
        
    #这里就是保存图片按钮了
    b02=tk.Button(a,text='左侧输入,点此保存',command=button02)
    b02.grid(row=0,column=1,stick=tk.EW)
    
    
    a.mainloop()
    

    一个实现好的作品

    我懒了,懒得写了,下面是我的一个完成品,界面简洁,功能也实现了不少。

    import tkinter as tk
    import os
    from PIL import Image, ImageTk
    import cv2 as cv
    from tkinter import filedialog
    import numpy as np
    from functools import partial
    from tools import *
    # 下面的是为了直方图均衡化
    
    from matplotlib.figure import Figure
    from matplotlib.backends.backend_tkagg import NavigationToolbar2Tk, FigureCanvasTkAgg
    
    
    # 打开,保存,退出,转灰度
    # 几何变换:镜像,平移,剪切,旋转,缩放
    # 空间滤波:平滑处理,锐化处理
    
    # 直方图,计算,显示,均衡化
    # 频域变换:Fourier变换,离散余弦变换,反变换(显示频谱图)
    # 频域滤波:低通滤波,高通滤波
    
    # 以颜色直方图为特征,搜索本地图片,反馈相似图片并排序
    
    def buttonSaveImg(img, entry):
        path = entry.get()
        # try:
        img.save(path)
        win22 = tk.Toplevel()
        win22.rowconfigure(0, weight=1)
        win22.columnconfigure(0, weight=1)
        win22.geometry('200x200')
        tk.Message(win22, text='保存成功').grid()
        # except:
        #     win22 = tk.Toplevel()
        #     win22.geometry('200x200')
        #     tk.Message(win22, text='图片路径无效').grid()
        #     win22.mainloop()
    
    
    def gsnoise():
        global path
        if len(path) > 0:
            img = getimg(path)[1]
            size = img.shape
            noise = np.array(np.random.randint(20, size=size[0:-1]) - 10, dtype=np.uint8)
            noise = noise.reshape(size[0], size[1], 1)
            img = img + noise
            img = Image.fromarray(img)
            wings = tk.Toplevel()
            photo = ImageTk.PhotoImage(img)
            tk.Label(wings, image=photo).grid()
            wings.mainloop()
    
    
    def button1():
        global lab1, path
        path = filedialog.askopenfilename()
        if len(path) > 0:
            img = getimg(path)[0]
            photo = ImageTk.PhotoImage(img)
            if lab1 == None:
                lab1 = tk.Label(win, image=photo)
                lab1.image = photo
                # lab1.rowconfigure()
                lab1.grid(row=3, columnspan=5,sticky=tk.NSEW)
            else:
                lab1.configure(image=photo)
                lab1.image = photo
    
    
    def button2():
        global lab1, path
        path = entry1.get()
        print(path)
        if len(path) > 0:
            img = getimg(path)[0]
            photo = ImageTk.PhotoImage(img)
            if lab1 == None:
                lab1 = tk.Label(win, image=photo)
                lab1.image = photo
                lab1.grid(row=3, columnspan=5,sticky=tk.NSEW)
            else:
                lab1.configure(image=photo)
                lab1.image = photo
    
    
    def button21():  # 参考faster rcnn RandomHorizontalFlip
        global path
        if len(path) == 0:
            return 0
        img = getimg(path)[1]  # Image.fromarray
        img = np.flip(img, axis=1)
        img = Image.fromarray(img)
    
        win21 = tk.Toplevel()  # 此处不能写Tk()因为根部窗口只能有一个
    
        win21.rowconfigure((1), weight=1)
        win21.columnconfigure((0, 1,2), weight=1)
        tk.Label(win21, text='输入图片保存路径', width=20).grid(row=0, column=0,sticky=tk.EW)
        entry21 = tk.Entry(win21)
        entry21.grid(row=0, column=1,sticky=tk.EW)
        tk.Button(win21, width=20, text='保存', command=partial(buttonSaveImg, img, entry21)).grid(row=0, column=2,sticky=tk.EW)
        photo21 = ImageTk.PhotoImage(img)
        tk.Label(win21, image=photo21).grid(row=1, columnspan=3,sticky=tk.NSEW)
    
        win21.mainloop()
    
    
    def button22():
        if len(path) > 0:
            win22 = tk.Toplevel()
            win22.rowconfigure(0, weight=1)
            win22.columnconfigure(0, weight=1)
            win22.geometry('200x200')
            tk.Message(win22, text='想象一下,已经平移了,只不过我们以它的中心为参照点').grid()
            win22.mainloop()
    
    
    def button23():
        # 剪切这个功能因为需要获取图片的坐标,实现鼠标拖动剪切,实际上很不好做”
        pass
    
        # l1.update()
    
        # w.update()
    
    
    def button24():  # 旋转——做出拖动条
        global path
        if len(path)==0:
            return 0
        # 测试旋转框函数的可行性
    
        #     l1.configure(image=img1)
        a = tk.Toplevel(win)
        a.rowconfigure(3, weight=1)
        a.columnconfigure((0, 1), weight=1)
        img = Image.open(path)
        img = ImageTk.PhotoImage(img)
        # 这次我们换一个思路,先加最关键的控件
    
        # 最终画布
        l1 = tk.Label(a, image=img)
        l1.grid(row=3, columnspan=2,sticky=tk.NSEW)
    
        # 角度
        agl = tk.IntVar()
        agl.set(0)
    
        # 定义setangle函数,为滑块提供反馈
        def setangle(value):
            agl.set(value)
    
        # 滑块
        scale24 = tk.Scale(a, from_=0, to=360, orient=tk.HORIZONTAL, showvalue=0, tickinterval=45,
                           resolution=1, command=setangle)
        scale24.grid(row=2, columnspan=2, sticky=tk.EW)
    
        # 按钮函数组件1:旋转图像函数
        def rotimg():
            # angle.path
            img = cv.imread(path)
            angle = agl.get()
            (h, w) = img.shape[:2]
            center = (w // 2, h // 2)
            M = cv.getRotationMatrix2D(center, angle, 1.0)
            rotedimg = cv.warpAffine(img, M, (w, h))
            rgb_img = cv.cvtColor(rotedimg, cv.COLOR_BGR2RGB)
            img = Image.fromarray(rgb_img)
            return img
    
        # 利用组件函数,构建按钮函数
        def button24():
            img = rotimg()
            # img.show()
            img = ImageTk.PhotoImage(img)
            l1.configure(image=img)
            a.mainloop()
    
        # 按钮控件
        b1 = tk.Button(a, width=20, command=button24, text='显示旋转图')
        b1.grid(row=1, column=1, sticky=tk.EW)
    
        # 角度显示盘
        la = tk.Label(a, textvariable=str(agl), bg='pink')
        la.grid(row=1, column=0, sticky=tk.EW)
    
        # 好的,既然我们实现了困难的部分,下面开始实现简单的部分,保存路径输入框entry
        entry01 = tk.Entry(a)
        entry01.grid(row=0, column=0, sticky=tk.EW)
    
        # 下面的部分是保存图片的按钮,会利用到之前的rotimg函数,和之前一样,先写按钮函数
        def button02():
            path = entry01.get()
            if len(path) == 0:
                print('保存失败')
                return 0
            img = rotimg()
            img.save(path)
            # 保存成功我们希望有个弹窗
            winmessage = tk.Toplevel(a)
            winmessage.rowconfigure(0, weight=1)
            winmessage.columnconfigure(0, weight=1)
            winmessage.geometry('300x200')
            tk.Message(winmessage, text='恭喜你,或者说恭喜这个熬了很久的猿,你们保存成功了').grid(sticky=tk.NSEW)
    
        # 这里就是保存图片按钮了
        b02 = tk.Button(a, text='左侧输入,点此保存', command=button02, bg='pink')
        b02.grid(row=0, column=1, sticky=tk.EW)
    
        a.mainloop()
    
    
    def button25():    # 缩放,我们希望设置两个方法——输入缩放比或者做成可视化的那种拖动条
        global path
        if len(path)==0:
            return 0
        # 缩放功能
        a = tk.Toplevel()
        a.rowconfigure(3, weight=1)
        a.columnconfigure((0, 1), weight=1)
        img = Image.open(path)
        img = ImageTk.PhotoImage(img)
    
        # 显而易见,当我们已经可以实现旋转功能时,缩放就不在话下了
        # 第一步:定义图片展示窗口(毕竟咱是按照逻辑顺序)
    
        # 最终画布
        l31 = tk.Label(a, image=img)
        l31.grid(row=3, columnspan=2,sticky=tk.NSEW)
    
        # 比例
        rate = tk.DoubleVar()
        rate.set(1.0)
    
        # 定义setangle函数,为滑块提供反馈
        def setangle(value):
            rate.set(value)
    
        # 滑块
        scale24 = tk.Scale(a, from_=1, to=5, orient=tk.HORIZONTAL, showvalue=1, tickinterval=1,
                           resolution=0.1, command=setangle)
        scale24.grid(row=2, columnspan=2, sticky=tk.EW)
    
        # 定义组件函数,实现缩放功能
        def shrink():
            # 收缩比例,路径
            r = rate.get()
            img = Image.open(path)
            size = img.size
            #     print(size)
            img2 = img.resize((int(size[0] / r), int(size[1] / r)))
            return img2
    
        def button22():
            img = shrink()
            #     img.show()
            img = ImageTk.PhotoImage(img)
            l31.configure(image=img)
            a.mainloop()
    
        # 按钮控件
        b22 = tk.Button(a, width=20, command=button22, text='显示缩放图')
        b22.grid(row=1, column=1, sticky=tk.EW)
    
        # 角度显示盘
        l21 = tk.Label(a, textvariable=str(rate), bg='pink')
        l21.grid(row=1, column=0, sticky=tk.EW)
    
        # 好的,既然我们实现了困难的部分,下面开始实现简单的部分,保存路径输入框entry
        entry01 = tk.Entry(a)
        entry01.grid(row=0, column=0, sticky=tk.EW)
    
        # 下面的部分是保存图片的按钮,会利用到之前的rotimg函数,和之前一样,先写按钮函数
        def button02():
            path = entry01.get()
            if len(path) == 0:
                print('保存失败')
                return 0
            img = shrink()
            img.save(path)
            # 保存成功我们希望有个弹窗
            winmessage = tk.Toplevel(a)
            winmessage.rowconfigure(0, weight=1)
            winmessage.columnconfigure(0, weight=1)
            winmessage.geometry('300x200')
            tk.Message(winmessage, text='恭喜你,或者说恭喜这个熬了很久的猿,你们保存成功了').grid(sticky=tk.NSEW)
    
        # 这里就是保存图片按钮了
        b02 = tk.Button(a, text='左侧输入,点此保存', command=button02, bg='pink')
        b02.grid(row=0, column=1, sticky=tk.EW)
    
        a.mainloop()
    
        # 至此,缩放功能实现完毕button25
    
    
    def button31():  # 平滑(模糊)
        # 平滑功能
        global path
        if len(path) is 0:
            return 0
        a = tk.Toplevel()
        a.rowconfigure(3, weight=1)
        a.columnconfigure((0, 1), weight=1)
    
        img = Image.open(path)
        img = ImageTk.PhotoImage(img)
    
        # 显而易见,当我们已经可以实现旋转功能时,缩放就不在话下了
        # 第一步:定义图片展示窗口(毕竟咱是按照逻辑顺序)
        kernels = [np.ones((i, i), np.float32) / (i * i) for i in range(1, 11)]
        # 最终画布
        l31 = tk.Label(a, image=img)
        l31.grid(row=3, columnspan=2,sticky=tk.NSEW)
    
        # 卷积核的大小比例
        rate = tk.IntVar()
        rate.set(1)
    
        # 定义setangle函数,为滑块提供反馈
        def setkernel(value):
            rate.set(value)
    
        # 滑块
        scale31 = tk.Scale(a, from_=1, to=11, orient=tk.HORIZONTAL, showvalue=1, tickinterval=2,
                           command=setkernel)
        scale31.grid(row=2, columnspan=2, sticky=tk.EW)
    
        # 定义组件函数,实现缩放功能
        def averaging():
            # 核大小,图片
            r = (rate.get() - 1) // 2
            img = cv.imread(path)
            kernel = kernels[r]
            avgimg = cv.filter2D(img, -1, kernel)
            rgb_img = cv.cvtColor(avgimg, cv.COLOR_BGR2RGB)
            img = Image.fromarray(rgb_img)
            return img
    
        def button22():
            img = averaging()
            img = ImageTk.PhotoImage(img)
            l31.configure(image=img)
            a.mainloop()
    
        # 按钮控件
        b22 = tk.Button(a, width=20, command=button22, text='显示缩放图')
        b22.grid(row=1, column=1, sticky=tk.EW)
    
        # 显示盘
        l21 = tk.Label(a, textvariable=str(rate), bg='pink')
        l21.grid(row=1, column=0, sticky=tk.EW)
    
        # 好的,既然我们实现了困难的部分,下面开始实现简单的部分,保存路径输入框entry
        entry01 = tk.Entry(a)
        entry01.grid(row=0, column=0, sticky=tk.EW)
    
        # 下面的部分是保存图片的按钮,会利用到之前的rotimg函数,和之前一样,先写按钮函数
        def button02():
            path = entry01.get()
            if len(path) == 0:
                print('保存失败')
                return 0
            img = averaging()
            img.save(path)
            # 保存成功我们希望有个弹窗
            winmessage = tk.Toplevel(a)
            winmessage.rowconfigure(0, weight=1)
            winmessage.columnconfigure(0, weight=1)
            winmessage.geometry('300x200')
            tk.Message(winmessage, text='恭喜你,或者说恭喜这个熬了很久的猿,你们保存成功了').grid(sticky=tk.NSEW)
    
        # 这里就是保存图片按钮了
        b02 = tk.Button(a, text='左侧输入,点此保存', command=button02)
        b02.grid(row=0, column=1, sticky=tk.EW)
    
        a.mainloop()
    
        # 至此,平滑功能实现完毕
    
    
    def button32():  # 锐化
        # 锐化功能
        global path
        if len(path) is 0:
            return 0
        a = tk.Toplevel()
        a.rowconfigure(3,weight=1)
        a.columnconfigure((0, 1), weight=1)
        # global path
        img = Image.open(path)
        img = ImageTk.PhotoImage(img)
    
        # 显而易见,当我们已经可以实现旋转功能时,缩放就不在话下了
        # 第一步:定义高通滤波核
        kernel0 = -np.ones((3, 3), np.float32)
        kernel0[1, 1] += 9
        kernel1 = -np.ones((5, 5), np.float32)
        kernel1[1:4, 1:4] = -kernel0
        kernel1[2, 2] = 8
        kernel2 = -np.ones((5, 5), np.float32)
        kernel2[2, 2] += 25
        kernels = [kernel0, kernel1, kernel2]
    
        # 最终画布
        l31 = tk.Label(a, image=img)
        l31.grid(row=3, columnspan=2,sticky=tk.NSEW)
    
        # 卷积核的大小比例
        rate = tk.IntVar()
        rate.set(1)
    
        # 定义setangle函数,为滑块提供反馈
        def setkernel(value):
            rate.set(value)
    
        # 滑块
        scale31 = tk.Scale(a, from_=1, to=3, orient=tk.HORIZONTAL, showvalue=1, tickinterval=1,
                           resolution=1, command=setkernel)
        scale31.grid(row=2, columnspan=2, sticky=tk.EW)
    
        # 定义组件函数,实现缩放功能
        def averaging():
            # 核大小,图片
            r = rate.get() - 1
            img = cv.imread(path)
            kernel = kernels[r]
            avgimg = cv.filter2D(img, -1, kernel)
            rgb_img = cv.cvtColor(avgimg, cv.COLOR_BGR2RGB)
            img = Image.fromarray(rgb_img)
            return img
    
        def button22():
            img = averaging()
            img = ImageTk.PhotoImage(img)
            l31.configure(image=img)
            a.mainloop()
    
        # 按钮控件
        b22 = tk.Button(a, width=20, command=button22, text='显示缩放图')
        b22.grid(row=1, column=1, sticky=tk.EW)
    
        # 显示盘
        l21 = tk.Label(a, textvariable=str(rate), bg='pink')
        l21.grid(row=1, column=0, sticky=tk.EW)
    
        # 好的,既然我们实现了困难的部分,下面开始实现简单的部分,保存路径输入框entry
        entry01 = tk.Entry(a)
        entry01.grid(row=0, column=0, sticky=tk.EW)
    
        # 下面的部分是保存图片的按钮
        def button02():
            path = entry01.get()
            if len(path) == 0:
                print('保存失败')
                return 0
            img = averaging()
            img.save(path)
            # 保存成功我们希望有个弹窗
            winmessage = tk.Toplevel(a)
            winmessage.rowconfigure(0, weight=1)
            winmessage.columnconfigure(0, weight=1)
            winmessage.geometry('300x200')
            tk.Message(winmessage, text='恭喜你,或者说恭喜这个熬了很久的猿,你们保存成功了').grid(sticky=tk.NSEW)
    
        # 这里就是保存图片按钮了
        b02 = tk.Button(a, text='左侧输入,点此保存', command=button02)
        b02.grid(row=0, column=1, sticky=tk.EW)
    
        a.mainloop()
    
        # 至此,锐化功能实现完毕
    
    
    def button23():  # 直方图均衡化
        global path
        if len(path) is 0:
            return 0
        # 利用cv内置工具获得直方图,已经均衡化后的图像
        img_ori=cv.imread(path)
        for i in range(3):
            im=img_ori[:,:,i]
            equ=cv.equalizeHist(im)
            try:
                im_box.append(equ)
            except:
                im_box=[equ]
        im_box=np.stack((im_box[2],im_box[1],im_box[0]),axis=2)
        #获得直方图三尺度均衡化后的图片
        rgb_im=Image.fromarray(im_box)
        #rgb_im.show()
        img = cv.imread(path, 0)
        hist = cv.calcHist([img], [0], None, [256], [0, 256])
        equ = cv.equalizeHist(img)
        histequ = cv.calcHist([equ], [0], None, [256], [0, 256])
    
        # 第二步,把这些图像放到一个figure上
        f = Figure(figsize=(10, 8), dpi=100)
        a11 = f.add_subplot(221)  # 添加子图:1行1列第1个
        # a11.title='原图像素值直方图'
        x = np.arange(256)
        y = hist
        a11.plot(x, y)
    
        a12 = f.add_subplot(222)
        # a12.title='原图灰度图'
        rgb_img = cv.cvtColor(img, cv.COLOR_BGR2RGB)
        a12.imshow(rgb_img)
    
        a21 = f.add_subplot(223)
        # a21.title('均衡化后的直方图')
        y = histequ
        a21.plot(x, y)
    
        a22 = f.add_subplot(224)
        # a22.title('均衡化后的图像')
        rgb_equ = cv.cvtColor(equ, cv.COLOR_BGR2RGB)
        a22.imshow(rgb_equ)
    
        # 第三步,建立和tkinter的联系,提供保存方法
        #显示均衡化的彩图
        def button10():
            img=rgb_im
            aa=tk.Toplevel()
            img=ImageTk.PhotoImage(img)
            aal=tk.Label(aa,image=img)
            aal.grid()
            aa.mainloop()
    
        #保存均衡化彩图
        def button11():
            path = entry00.get()
            rgb_im.save(path)
            winmessage = tk.Toplevel(a)
            winmessage.geometry('300x200')
            tk.Message(winmessage, text='恭喜你,或者说恭喜这个熬了很久的猿,你们保存成功了').grid(sticky=tk.NSEW)
    
        a = tk.Toplevel()
        a.rowconfigure((2),weight=1)
        a.columnconfigure((0,1),weight=1)
        img_real=ImageTk.PhotoImage(rgb_im)
        b = tk.Frame(a)
        canvas1 = FigureCanvasTkAgg(f, b)
        canvas1.draw()
        canvas1.get_tk_widget().grid()
        b.grid(row=2, columnspan=2, sticky=tk.NSEW)
        b10=tk.Button(a,text='展示均衡化彩图',command=button10).grid(row=1,column=0,sticky=tk.EW)
        b11=tk.Button(a,text='保存均衡化彩图',command=button11).grid(row=1,column=1,sticky=tk.EW)
        # 提供保存方法
        def button01():
            path = entry00.get()
            cv.imwrite(path, equ)
            winmessage = tk.Toplevel(a)
            winmessage.rowconfigure(0, weight=1)
            winmessage.columnconfigure(0, weight=1)
            winmessage.geometry('300x200')
            tk.Message(winmessage, text='恭喜你,或者说恭喜这个熬了很久的猿,你们保存成功了').grid(sticky=tk.NSEW)
    
        entry00 = tk.Entry(a)
        entry00.grid(row=0, column=0, sticky=tk.EW)
        b01 = tk.Button(a, text='点此保存均衡化的图片', command=button01)
        b01.grid(row=0, column=1,sticky=tk.EW)
        a.mainloop()
    
    def button33():#低通滤波
        '''低通滤波,基本组件:
    
            文字框,保存图片按钮
    
            滑块数值展示,展示图片
    
            滑块
    
            图片'''
        # 判断函数
        if len(path) is 0:
            return 0
        if path.endswith('.jpg') is not True:
            return 0
        # 读取图片
        a = tk.Toplevel()
        img = Image.open(path)
        img = ImageTk.PhotoImage(img)
    
        # 可以调节界面
        a.rowconfigure(3, weight=1)
        for j in range(2):
            a.columnconfigure(j, weight=1)
        # 画布
        l30 = tk.Label(a, image=img)
        l30.grid(row=3, columnspan=2, sticky=tk.NSEW)
    
        # 定义一个变量
        kernelwidth = tk.IntVar()
        kernelwidth.set(1)
    
        # 定义滑块函数
        def setwidth(value):
            kernelwidth.set(value)
    
        # 定义滑块
        scale20 = tk.Scale(a, from_=1, to=5, orient=tk.HORIZONTAL, showvalue=1,
                           resolution=1, tickinterval=1, command=setwidth)
        scale20.grid(row=2, columnspan=2, sticky=tk.EW)
    
        # 滑块数值显示器
        l10 = tk.Label(a, textvariable='滤波核宽度为' + str(kernelwidth.get() * 20), bg='pink')
        l10.grid(row=1, column=0, sticky=tk.EW)
    
        # 按钮函数组件1:低通滤波器
        def low_filter():
            width = kernelwidth.get() * 10
            img = cv.imread(path)
            rows, cols = img.shape[:2]
            mask = np.zeros((rows,cols), np.uint8)
            mask[(rows // 2 - width):(rows // 2 + width)][:, (cols // 2 - width):(cols // 2 + width)] = 1
            img_l = [1, 2, 3]
            for i in range(3):
                f1 = np.fft.fft2(img[:, :, i])
                f1shift = np.fft.fftshift(f1) * mask
                f2shift = np.fft.ifftshift(f1shift)
                img_new = np.fft.ifft2(f2shift)
                img_new = np.abs(img_new)
                img_new = (img_new - np.amin(img_new)) / (np.amax(img_new) - np.amin(img_new))
                img_new *= 255
                img_l[i] = np.array(img_new, np.uint8)
            img = np.stack((img_l[0], img_l[1], img_l[2]), axis=2)
            img = cv.cvtColor(img, cv.COLOR_BGR2RGB)
            img = Image.fromarray(img)
            return img
    
        def button11():
            img = low_filter()
            img = ImageTk.PhotoImage(img)
            l30.configure(image=img)
            a.mainloop()
    
        b11 = tk.Button(a, text='点此展示滤波效果', command=button11)
        b11.grid(row=1, column=1, sticky=tk.EW)
    
        # 保存
        entry01 = tk.Entry(a)
        entry01.grid(row=0, column=0, sticky=tk.EW)
    
        # 下面的部分是保存图片的按钮,会利用到之前的rotimg函数,和之前一样,先写按钮函数
        def button02():
            path = entry01.get()
            if len(path) == 0:
                print('保存失败')
                return 0
            img = low_filter()
            img.save(path)
            # 保存成功我们希望有个弹窗
            winmessage = tk.Toplevel(a)
            winmessage.rowconfigure(0, weight=1)
            winmessage.columnconfigure(0, weight=1)
            winmessage.geometry('300x200')
            tk.Message(winmessage, text='恭喜你,或者说恭喜这个熬了很久的猿,你们保存成功了').grid(sticky=tk.NSEW)
    
        # 这里就是保存图片按钮了
        b02 = tk.Button(a, text='左侧输入,点此保存', command=button02)
        b02.grid(row=0, column=1, sticky=tk.EW)
    
        a.mainloop()
    def button35():#低通滤波
        # 判断函数
        if len(path) is 0:
            return 0
        if path.endswith('.jpg') is not True:
            return 0
        # 读取图片
        a = tk.Toplevel()
        img = Image.open(path)
        img = ImageTk.PhotoImage(img)
    
        # 可以调节界面
    
        a.rowconfigure(3, weight=1)
        for j in range(2):
            a.columnconfigure(j, weight=1)
        # 画布
        l30 = tk.Label(a, image=img)
        l30.grid(row=3, columnspan=2, sticky=tk.NSEW)
    
        # 定义一个变量
        kernelwidth = tk.IntVar()
        kernelwidth.set(1)
    
        # 定义滑块函数
        def setwidth(value):
            kernelwidth.set(value)
    
        # 定义滑块
        scale20 = tk.Scale(a, from_=1, to=5, orient=tk.HORIZONTAL, showvalue=1,
                           resolution=1, tickinterval=1, command=setwidth)
        scale20.grid(row=2, columnspan=2, sticky=tk.EW)
    
        # 滑块数值显示器
        l10 = tk.Label(a, textvariable='滤波核宽度为' + str(kernelwidth.get() * 20), bg='pink')
        l10.grid(row=1, column=0, sticky=tk.EW)
    
        # 按钮函数组件1:低通滤波器
        def low_filter():
            width = kernelwidth.get() * 10
            img = cv.imread(path)
            rows, cols = img.shape[:2]
            mask = np.ones((rows,cols), np.uint8)
            mask[(rows // 2 - width):(rows // 2 + width)][:, (cols // 2 - width):(cols // 2 + width)] = 0
            img_l = [1, 2, 3]
            for i in range(3):
                f1 = np.fft.fft2(img[:, :, i])
                f1shift = np.fft.fftshift(f1) * mask
                f2shift = np.fft.ifftshift(f1shift)
                img_new = np.fft.ifft2(f2shift)
                img_new = np.abs(img_new)
                img_new = (img_new - np.amin(img_new)) / (np.amax(img_new) - np.amin(img_new))
                img_new *= 255
                img_l[i] = np.array(img_new, np.uint8)
            img = np.stack((img_l[0], img_l[1], img_l[2]), axis=2)
            img = cv.cvtColor(img, cv.COLOR_BGR2RGB)
            img = Image.fromarray(img)
            return img
    
        def button11():
            img = low_filter()
            img = ImageTk.PhotoImage(img)
            l30.configure(image=img)
            a.mainloop()
    
        b11 = tk.Button(a, text='点此展示滤波效果', command=button11)
        b11.grid(row=1, column=1, sticky=tk.EW)
    
        # 保存
        entry01 = tk.Entry(a)
        entry01.grid(row=0, column=0, sticky=tk.EW)
    
        # 下面的部分是保存图片的按钮,会利用到之前的rotimg函数,和之前一样,先写按钮函数
        def button02():
            path = entry01.get()
            if len(path) == 0:
                print('保存失败')
                return 0
            img = low_filter()
            img.save(path)
            # 保存成功我们希望有个弹窗
            winmessage = tk.Toplevel(a)
            winmessage.rowconfigure(0, weight=1)
            winmessage.columnconfigure(0, weight=1)
            winmessage.geometry('300x200')
            tk.Message(winmessage, text='恭喜你,或者说恭喜这个熬了很久的猿,你们保存成功了').grid(sticky=tk.NSEW)
    
        # 这里就是保存图片按钮了
        b02 = tk.Button(a, text='左侧输入,点此保存', command=button02)
        b02.grid(row=0, column=1, sticky=tk.EW)
    
        a.mainloop()
    
    win = tk.Tk()
    win.rowconfigure((3),weight=1)
    win.columnconfigure((0,1,2,3,4),weight=1)
    path = ''
    lab1 = tk.Label(win,width=30,height=30).grid(row=3,column=0,sticky=tk.NSEW,columnspan=5)
    
    entry1 = tk.Entry(win, width=20)
    entry1.grid(row=0, column=1, sticky=tk.EW)
    lab2 = tk.Label(win, text='输入图片路径', width=20).grid(row=0, column=0,sticky=tk.EW)
    lab2 = tk.Label(win, text='手动选择路劲', width=20).grid(row=0, column=3,sticky=tk.EW)
    
    b1 = tk.Button(win, text='选择路径', command=button1, width=20).grid(row=0, column=4,sticky=tk.EW)
    b2 = tk.Button(win, text='确认路径', command=button2, width=20).grid(row=0, column=2,sticky=tk.EW)
    
    b21 = tk.Button(win, text='镜像', command=button21, width=20).grid(row=1, column=0,sticky=tk.EW)
    b22 = tk.Button(win, text='平移', command=button22, width=20).grid(row=1, column=1,sticky=tk.EW)
    b23 = tk.Button(win, text='直方图均衡化', command=button23, width=20).grid(row=1, column=2,sticky=tk.EW)
    b24 = tk.Button(win, text='顺时针旋转', command=button24, width=20).grid(row=1, column=3,sticky=tk.EW)
    b25 = tk.Button(win, text='缩放', command=button25, width=20).grid(row=1, column=4,sticky=tk.EW)
    
    b31 = tk.Button(win, text='图像平滑', command=button31, width=20).grid(row=2, column=0,sticky=tk.EW)
    b32 = tk.Button(win, text='图像锐化', command=button32, width=20).grid(row=2, column=1,sticky=tk.EW)
    b33 = tk.Button(win, text='低通滤波', command=button33, width=20).grid(row=2, column=2,sticky=tk.EW)
    b34 = tk.Button(win, text='高斯噪声', command=gsnoise, width=20).grid(row=2, column=3,sticky=tk.EW)
    b35 = tk.Button(win, text='高通滤波', command=button35, width=20).grid(row=2, column=4,sticky=tk.EW)
    win.mainloop()
    
    
    物联沃分享整理
    物联沃-IOTWORD物联网 » 使用Python和OpenCV创建tkinter交互式图像处理应用程序

    发表评论