Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
339 views
in Technique[技术] by (71.8m points)

python - Image editing software with Tkinter and openCV, and how do make a button that reaches a function and displaying it

So as the title says, I'm working on a very basic image editor project using Tkinter and openCV with the ability to rotate the image left and right, do a histogram equalization, apply a median filter, etc, so far the only thing that works is displaying 2 images in the appropriate panels and an option to import it in greyscale. The panelSRC should display the input image and panelDST should display the output image The "gray?" checkbox works fine. I think the errors I get are function related errors but they are so presistent, for example:

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

would give an error of

Unresolved reference

Well that would get fixed if I remove the indentations, BUT that will not make it work upon pressing the button which is the goal I want to reach

Even if the function is right there above it, it still marks the rotate_right with an error, fix it by putting no indentations for the rotate_right function and write it without spaces? Well, that will not make the button do its work when it is pressed.

Do I put the parameter image in the function (e.g: def rotateleft(image):)? No still errors

*line 1883, in call return self.func(args) TypeError: rotate_left() missing 1 required positional argument: 'image' Changing the places of functions? Same problem. Here is the full code

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)'''

I was told to change:

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

into

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

but still didn't work (and I don't quite understand why he said that, something about nested functions but I don't know `

Note that out of despair, I stripped down the roate_left function to a minimal form for the convenience of reading and helping, before that, I really filled it with all types of stuff I tried to make the GUI part as an object (class) and without too, nothing

I've been through a loop of trial and error for days and still not work, I've tried everything, putting panelSRC and panelDST as parameters in the rotate_left function, copy-pasting the entire select_image function into the rotate_left function, and so on

And as an extra try to be ensured, how do I make the flip work upon button press?

TL;DR: How do I make the button in Tkinter reach a function that would be put (where do I use it?) and after that the function displays the edited value in panelDST


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Answer

0 votes
by (71.8m points)

Firstly as others pointed out to bind a Button widget to an event handler using a command parameter you are not supposed to assign as a string.

You are also making the same mistake a lot of people make I.e. Creating the Label widget, again and again, instead of reusing the existing one by using the configure method.

Coming to the part of rotating the image, Firstly, you should assign cv2.imread(path, cv2.COLOR_BGR2RGB) to a global variable like shown below, and do not reuse this variable unnecessarily as you will be using the same variable for rotating the image. To rotate an image simply assign img = cv2.rotate(img, rotate_direction), this will return a numpy array which you can later convert to an image and display in the GUI.

To exit the GUI simply assign the exit button's command parameter as root.destroy

Here is the corrected code.

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() 

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...