带有Tkinter和openCV的图像编辑软件,以及如何制作一个达到功能并显示它的按钮

Posted

技术标签:

【中文标题】带有Tkinter和openCV的图像编辑软件,以及如何制作一个达到功能并显示它的按钮【英文标题】:Image editing software with Tkinter and openCV, and how do make a button that reaches a function and displaying it 【发布时间】:2021-04-07 00:48:28 【问题描述】:

正如标题所说,我正在使用 Tkinter 和 openCV 开发一个非常基本的图像编辑器项目,它能够左右旋转图像、进行直方图均衡、应用中值滤波器等,到目前为止唯一可行的方法是在适当的面板中显示 2 个图像,并选择以灰度导入它。 panelSRC 应显示输入图像,而 panelDST 应显示输出图像 “灰色?”复选框工作正常。 我认为我得到的错误是与功能相关的错误,但它们是如此持久,例如:

rotanticlk = tk.Button(root, text="RotLeft", command=rotate_left)

会报错

Unresolved reference

好吧,如果我删除缩进,这会得到解决,但是按下按钮后它不会起作用,这是我想要达到的目标

即使该函数就在它上面,它仍然将rotate_right标记为错误,通过不为rotate_right函数放置缩进来修复它并且写它没有空格?好吧,这不会使按钮在按下时发挥作用。

我是否将参数图像放入函数中(例如:def rotateleft(image):)?没有仍然错误

*第 1883 行,在 call 中返回 self.func(args) 类型错误:rotate_left() 缺少 1 个必需的位置参数:'image' 改变功能的地方?同样的问题。 这是完整的代码

import tkinter as tk
from PIL import Image
from PIL import ImageTk
from tkinter import filedialog
import cv2
import numpy as np


def select_image():
    # grab a reference to the image panels
    global panelSRC, panelDST

    path = filedialog.askopenfilename()

    # ensure a file path was selected
    if len(path) > 0:
        # load the image from disk, convert it to grayscale, and detect
        # edges in it
        image = cv2.imread(path)
        edited = image*1

        if vargray.get() == 1:
            image = cv2.imread(path, 0)

        # OpenCV represents images in BGR order; however PIL represents
        # images in RGB order, so we need to swap the channels
        image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)

        # convert the images to PIL format...
        image = Image.fromarray(image)
        edited = Image.fromarray(edited)

        # ...and then to ImageTk format
        image = ImageTk.PhotoImage(image)
        edited = ImageTk.PhotoImage(edited)

        # if the panels are None, initialize them
        if panelSRC is None or panelDST is None:
            # the first panel will store our original image
            panelSRC = tk.Label(image=image)
            panelSRC.image = image
            panelSRC.pack(side="left", padx=10, pady=10)

            # while the second panel will store the edge map
            panelDST = tk.Label(image=edited)
            panelDST.image = edited
            panelDST.pack(side="right", padx=10, pady=10)

        # otherwise, update the image panels
        else:
            # update the pannels
            panelSRC.configure(image=image)
            panelDST.configure(image=edited)
            panelSRC.image = image
            panelDST.image = edited


def rotate_left(image):
    panelDST = None
    edited=cv2.rotate(image,90)
    panelDST = tk.Label(image=edited)



# initialize the window toolkit along with the two image panels
root = tk.Tk()
panelSRC = None
panelDST = None

# create a button, then when pressed, will trigger a file chooser
# dialog and allow the user to select an input image; then add the
# button the GUI
btn = tk.Button(root, text="Select an image", command=select_image)
vargray = tk.IntVar()
chkbtn = tk.Checkbutton(root, text="gray?", variable=vargray)
btn.pack(side="bottom", fill="y", expand="yes", padx="10", pady="10")
chkbtn.pack()
root.title('toolbox')  # window title is toolbox
w = tk.Label(root, text="Hi, Welcome to Zeiad's toolbox !")
rotclk = tk.Button(root, text="RotRight", command="rotate_right")
brightup = tk.Button(root, text="RotLeft", command="buttonpressed")
brightdown = tk.Button(root, text="RotLeft", command="buttonpressed")
Exit1 = tk.Button(root, text="Exit", command="exitbutton")

w.pack()  # Packs the elements (widgets) so that the window takes thesize of them only, so it ensures that the widget stays exactly where it issupposed to be
btn.pack(side="bottom", fill="both", expand="yes", padx="10", pady="10")
rotclk.pack(side='right')
rokanticlk.pack(side='left')
Exit1.pack(side='bottom')

root.mainloop()  # similar to cv2.waitKey(0)

##FLIP##
'''def flip():
cv2.imshow("original",image)
fl=cv2.flip(image,-1)
cv2.imshow("flip",fl)
cv2.waitKey(0)'''

我被告知要改变:

rokanticlk = tk.Button(root, text="RotLeft", command="rotate_left")

进入

rokanticlk = tk.Button(root, text="RotLeft", command=rotate_left)

但仍然没有工作(我不太明白他为什么这么说,关于嵌套函数但我不知道 `

请注意,出于绝望,为了方便阅读和帮助,我将 roate_left 函数剥离为最小形式,在此之前,我真的用各种类型的东西填充它 我试图将 GUI 部分作为一个对象(类)而没有,什么都没有

我已经经历了几天的反复试验,但仍然无法正常工作,我已经尝试了所有方法,将 panelSRC 和 panelDST 作为参数放入 rotate_left 函数中,复制粘贴整个select_image 函数转化为 rotate_left 函数,以此类推

为了确保额外的尝试,如何在按下按钮时进行翻转?

TL;DR:如何使 Tkinter 中的按钮达到将被放置的功能(我在哪里使用它?),然后该功能在 中显示 edited 值面板夏令时

【问题讨论】:

你注意到你的命令是字符串吗? command="buttonpressed" 应改为command=buttonpressed 正如我之前提到的,当我这样做时,它给出了一个错误,command=buttonpressed =>未解决的参考,我尝试通过更改函数的位置来修复该错误几次 def rotate_left 但是完全没有任何变化,尝试将其作为 def select_image(): 函数中的嵌套函数,因为它起作用了,我输入了edited=WhateverIWant,但就按钮按下到达该函数而言,它不起作用 你没有传入任何参数。但是您正在定义参数。事实上,我不明白你要做什么。您或许应该阅读 global 和本地范围。 我之前尝试过使 image/edited 全局化,但遗憾的是它不起作用,或者你的意思是别的什么? 我的意思是rotate_left() 接受了一个图像参数,但你没有传入任何图像。 【参考方案1】:

首先,正如其他人指出的那样,使用您不应该分配为字符串的命令参数将Button 小部件绑定到事件处理程序。

你也犯了很多人犯的同样的错误,即一次又一次地创建Label 小部件,而不是使用configure 方法重用现有的小部件。

来到旋转图像的部分,首先,您应该将cv2.imread(path, cv2.COLOR_BGR2RGB) 分配给如下所示的全局变量,并且不要不必要地重复使用此变量,因为您将使用相同的变量来旋转图像。要旋转图像,只需分配 img = cv2.rotate(img, rotate_direction),这将返回一个 numpy 数组,您可以稍后将其转换为图像并显示在 GUI 中。

要退出 GUI,只需将退出按钮的命令参数指定为 root.destroy

这是更正后的代码。

import tkinter as tk
from PIL import Image
from PIL import ImageTk
from tkinter import filedialog
import cv2
import numpy as np


def select_image():

    global img

    path = filedialog.askopenfilename()


    if path:
    
        img = cv2.imread(path, cv2.COLOR_BGR2RGB)
        grayScale = ''
        
        image = Image.fromarray(img)
        image_tk = ImageTk.PhotoImage(image)


        if vargray.get() == 1:
            grayScale = cv2.imread(path, 0)  
            grayScale_img = Image.fromarray(grayScale)
            grayScale_img_tk = ImageTk.PhotoImage(grayScale_img)

            panelDST.configure(image=grayScale_img_tk)
            panelDST.image = grayScale_img_tk 
        
        panelSRC.configure(image=image_tk)
        panelSRC.image = image_tk
    
        

def rotate_left():
    global img

    if img.any():  #check whether image exists
        
        image = cv2.rotate(img, cv2.ROTATE_90_COUNTERCLOCKWISE)

        img = image
        
        image = Image.fromarray(image)
        image_tk = ImageTk.PhotoImage(image)

        panelSRC.configure(image=image_tk)
        panelSRC.image = image_tk


def rotate_right():
    global img
    if img.any():
        
        image = cv2.rotate(img, cv2.ROTATE_90_CLOCKWISE)

        img = image
        
        image = Image.fromarray(image)
        image_tk = ImageTk.PhotoImage(image)

        panelSRC.configure(image=image_tk)
        panelSRC.image = image_tk


root = tk.Tk()
root.title('toolbox')  # window title is toolbox


img = np.array([])  # set it to numpy array initially

vargray = tk.IntVar()
chkbtn = tk.Checkbutton(root, text="gray?", variable=vargray)
chkbtn.pack()

w = tk.Label(root, text="Hi, Welcome to Zeiad's toolbox !")
w.pack()

btn = tk.Button(root, text="Select an image", command=select_image)
btn.pack(side="bottom", fill="y", expand="yes", padx="10", pady="10")

rotclk = tk.Button(root, text="RotRight", command=rotate_right)
rotclk.pack(side='right')

rokanticlk = tk.Button(root, text='RotLeft', command=rotate_left)
rokanticlk.pack(side='left')

brightup = tk.Button(root, text="RotLeft", command="buttonpressed")
brightdown = tk.Button(root, text="RotLeft", command="buttonpressed")

Exit1 = tk.Button(root, text="Exit", command=root.destroy)

btn.pack(side="bottom", fill="both", expand="yes", padx="10", pady="10")
Exit1.pack(side='bottom')

panelSRC = tk.Label(root)
panelSRC.pack(side="left", padx=10, pady=10)

panelDST = tk.Label(root)
panelDST.pack(side="right", padx=10, pady=10)

root.mainloop() 

【讨论】:

OMG 是的,这太完美了,完美无瑕,非常感谢!是的,提供一些解释将非常有帮助,因为它可以帮助我理解如何添加新事物,例如添加伽玛(亮度)或负值的函数(即使它只是image=-255*image [当然适用于灰度]甚至是def Bup(): global img if img.any(): image = cv2.equalizeHist(img) img = image image = Image.fromarray(image) image_tk = ImageTk.PhotoImage(image) panelSRC.configure(image=image_tk) panelSRC.image = image_tk 这是巧妙的、伟大的工作! 我尝试添加到程序中的任何新内容最常见的错误是“函数名 img = image UnboundLocalError: local variable 'image' referenced before assignment”中的错误我完全按照你写的方式 rotate_right 和rotate_left 函数我很困惑为什么我写的其他函数不起作用 @Zeiad98 还有哪些其他功能?你是如何添加它们的? 例如分配一个具有 command=Hist 的按钮:def Hist(): global img if img.any(): image = cv2.equalizeHist(image) '''Here 'img' will not work bec this funcion must have a grayscale image, even if I change the 'panelDST' to 'panelSRC' in the panelDST.configure(image=grayScale_img_tk) panelDST.image = grayScale_img_tk ` line it won't work ''' img = image image = Image.fromarray(image) image_tk = ImageTk.PhotoImage(image) panelSRC.configure(image=image_tk) panelSRC.image = image_tk` def Hist(): global img if img.any(): image = cv2.equalizeHist(image) img = image image = Image.fromarray(image) image_tk = ImageTk.PhotoImage(image) panelSRC.configure(image=image_tk) panelSRC.image = image_tk 给出一个 'UnboundLocalError: local variable 'image' referenced before assignment' 错误与 gamma 相同(根据 gamme 的值增加/减少亮度的电源转换代码

以上是关于带有Tkinter和openCV的图像编辑软件,以及如何制作一个达到功能并显示它的按钮的主要内容,如果未能解决你的问题,请参考以下文章

使用 OpenCV 读取图像并使用 Tkinter 显示

Python,在标签中的 Tkinter 中显示 openCv 图像

Tkinter 自定义创建按钮

tkinter:打开一个带有按钮提示的新窗口[关闭]

opencv+tkinter制作HsvMaster

如何使用 tkinter 比例修改图像?