Python for Beginners: Creating an OpenCV App in Python

A couple of weeks ago, I published a blog on creating a GUI in Python using Tkinter. I thought of kicking it up a notch and integration the function of OpenCV in my Python app. So I decided to create a small desktop app where you can upload an image and convert it to grayscale with a click of a button. So get your IDE ready, grab a cup of coffee, and let’s turn our code into creativity!

Tkinter Setup

I assume that you are already familiar with Tkinter. If not, stop reading further and first go check out my previous blog on creating a GUI using Tkinter. If you already have heard of Tkinter but need a quick revision of the library, here’s a brief description:

Tkinter is a Python package for creating GUI applications. Python has a lot of GUI frameworks, but Tkinter is lightweight and relatively painless to use compared to other frameworks. This makes it a compelling choice for building GUI applications in Python, especially for applications where a modern shine is unnecessary, and the top priority is to build something functional and cross-platform quickly. 

Getting Started with the Backend

To keep the functioning of the app systematic, I thought of creating two Python files for this project: one with act as a “frontend” or the “main” script which will contain the code for the GUI design. The other script will be the “backend”, which means all the callback functions that will be called when a button is pressed will be stored in this file. We’ll call this file as gui_func.py. Let us first start with gui_func file.

As mentioned earlier, I want to use this app to change a given RGB image into grayscale. Hence, we need two basic functions in our backend: one function to view an image and a second to convert that image into grayscale. This conversion will take place using opencv.

First, we will see the function of loading and displaying images.

from tkinter import *
import cv2
import os, sys
from PIL import Image, ImageTk

def displayImage(t, image_name):
    try:
        string_length = int(len(image_name))-1
        image_name = str(image_name[:string_length]) + ".jpg"
        img = cv2.imread(str(image_name))
        img = cv2.resize(img, (600, 550))
        cv2.imshow("Image", img)
        cv2.waitKey(3000)
        cv2.destroyAllWindows()

    except cv2.error:
        text = Label(t, text="Please enter a valid file name")
        text.place(x=70,y=90)

The primary purpose of displayImage function is to load and display an image whose filename is provided. The image filename is taken from a text box which is an entity of the Tkinter library (we will come across this again in the frontend code). The try-except block is used to handle potential errors that could occur during the process of loading and displaying the image. Specifically, if the image file specified by the user is not found or is not a valid image file that OpenCV can read, it will raise an exception. The except block catches this exception and provides feedback to the user by displaying a message (in this case, “Please enter a valid file name”) within the GUI window.

So, in summary, this function aims to load and display an image based on user input while also providing error handling to handle cases where the specified image file cannot be found or is invalid.

def displayGrayscaleImage(t, image):
    try:
        string_length = int(len(image))-1
        image = str(image[:string_length]) + ".jpg"
        image = cv2.imread(str(image))
        image = cv2.resize(image, (600, 550))
        gray_img = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
        cv2.imshow("Image", gray_img)
        cv2.waitKey(3000)
        cv2.destroyAllWindows()

    except cv2.error:
        text = Label(t, text="Please enter a valid file name")
        text.place(x=70,y=90)

def exit():
    sys.exit()

This function displayGrayscaleImage will take in the image that was loaded in the previous function. As you can see in the code, we use OpenCV to convert the image into grayscale and display it for three seconds. Similar to the above function, the try-except block will handle errors that may arise when loading and displaying the image.

Further, thought of integrating one small function exit to exit the app.

That’s it for the backend part. We implemented three functions, displayImage, displayGrayscaleImage and exit that our Python application will carry out in the background when the respective button is pressed. So let us now move on to the front-end programming.

Designing the GUI

For the user interface of the Python app, I thought of creating a window of 600×300 that will contain one text box to enter the image filename, and three buttons for different functions (for displaying the original image, displaying its grayscale form, and exiting the app). I do admit that this would not result in a great looking user interface, but would at least do the work, so that’s what’s important.

from tkinter import *
from gui_func import *

t = Tk()
t.geometry("600x300")
t.title("MLS GUI- Convert RGB to Grayscale")

fileOptions = ["New", "Open", "Save", "Save as...", "Import", "Export", "Exit"]
viewOptions = ["Transform", "Edit", "Create"]

menuBar = Menu(t)
file = Menu(menuBar, tearoff=0)

We first create an instance of Tkinter and save it as t. We then define its geometrical aspects and title.
The Menu method of Tkinter creates a menu bar for our app and file-related operations within that menu bar. We will not be assigning any functionality to those buttons, we’ll just show them for the sake of appearance.

inputtxt = Text(t, height = 2, width = 20)
inputtxt.grid(row=1, column=2)

Text creates a multi-line text input field with a height of 2 lines and a width of 20 characters. The grid method is then used to position this input field in row 1 and column 2 of the GUI window (t).

Now it’s time to create the buttons. Three buttons are created with different functionalities:

Button(t, text = "View Image", bg = "blue", fg = "white",
       height="1", width="10",
       command=lambda:displayImage(t,inputtxt.get(1.0, END)),
       relief=RAISED, borderwidth=6).grid(row=1, column=5)

Button(t, text = "Convert to Grayscale", bg = "blue", fg = "white",
       height="1", width="20",
       command=lambda:displayGrayscaleImage(t, inputtxt.get(1.0, END)),
       relief=RAISED, borderwidth=6).grid(row=1, column=6)

Button(t, text = "Exit", bg = "blue", fg = "white",
       height="1", width="10",
       command=lambda:exit(),
       relief=RAISED, borderwidth=6).grid(row=3, column=5)

Each button has specified characteristics such as text, background and foreground color, height, width, relief, and border width. The command parameter specifies the function to be called when the button is clicked. It uses lambda to pass arguments to the function. The grid method is used to position each button in the GUI window (t) at specified rows and columns.

Ultimately, we will start our application using the mainloop function.

t.mainloop()

Run the main frontend script from your project workspace and you should see an app as shown in the image below:

python for beginners

Save any JPG image in your workspace and enter its name in the text box of the Python app. When you click on “View Image”, you should see the original image. After clicking on “Convert to Grayscale”, you should see the grayscale version of the image and also a copy of it saved in your project workspace.

Complete Code

Backend

from tkinter import *
import cv2
import os, sys
from PIL import Image, ImageTk

def buttonPressed(t, text):
    Label(t, text="Button " + str(text)+" pressed.", font=(60)).grid(row=5, column=2)

def displayImage(t, image_name):
    try:
        string_length = int(len(image_name))-1
        image_name = str(image_name[:string_length]) + ".jpg"
        img = cv2.imread(str(image_name))
        img = cv2.resize(img, (600, 550))
        cv2.imshow("Image", img)
        cv2.waitKey(3000)
        cv2.destroyAllWindows()

    except cv2.error:
        text = Label(t, text="Please enter a valid file name")
        text.place(x=70,y=90)

def displayGrayscaleImage(t, image):
    try:
        string_length = int(len(image))-1
        image = str(image[:string_length]) + ".jpg"
        image = cv2.imread(str(image))
        image = cv2.resize(image, (600, 550))
        gray_img = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
        cv2.imshow("Image", gray_img)
        cv2.waitKey(3000)
        cv2.destroyAllWindows()

    except cv2.error:
        text = Label(t, text="Please enter a valid file name")
        text.place(x=70,y=90)

def exit():
    sys.exit()

Frontend

from tkinter import *
from gui_func import *

t = Tk()
t.geometry("600x300")
t.title("MLS GUI- Convert RGB to Grayscale")

fileOptions = ["New", "Open", "Save", "Save as...", "Import", "Export", "Exit"]
viewOptions = ["Transform", "Edit", "Create"]

menuBar = Menu(t)
file = Menu(menuBar, tearoff=0)
inputtxt = Text(t, height = 2, width = 20)
inputtxt.grid(row=1, column=2)

Button(t, text = "View Image", bg = "blue", fg = "white",
       height="1", width="10",
       command=lambda:displayImage(t,inputtxt.get(1.0, END)),
       relief=RAISED, borderwidth=6).grid(row=1, column=5)

Button(t, text = "Convert to Grayscale", bg = "blue", fg = "white",
       height="1", width="20",
       command=lambda:displayGrayscaleImage(t, inputtxt.get(1.0, END)),
       relief=RAISED, borderwidth=6).grid(row=1, column=6)

Button(t, text = "Exit", bg = "blue", fg = "white",
       height="1", width="10",
       command=lambda:exit(),
       relief=RAISED, borderwidth=6).grid(row=3, column=5)

t.mainloop()

Conclusion

In this blog, we saw how we can create a small Python application for a simple use case, for example converting an RGB image to grayscale. It is important to note the significant methods of Tkinter. Once familiar with the framework, one can create any entity, such as a button or a text box, and assign any particular function to it. As a form of challenge, consider extending this Python app and adding two to three more functionalities to it such as integrating YOLO object detection with a button or displaying the resolution of the imported image.

In case you encounter any issues during this exercise or some code that you did not understand, feel free to drop a message on my Instagram. You’ll find the links to my social media below. And, if you enjoyed this exercise, consider following me on my social media to get programming tips and small snippets:

You might also not want to miss any of such interesting posts, would you? Then join my FREE newsletter where you will get a monthly summary of the Machine Learning Site blog posts that you could have missed.

This Post Has 2 Comments

Leave a Reply