OpenCV with Tkinter

Figure 6: On the left, we have the original, unprocessed image. Then, on the right, we have the edge map.

I’ll be honest with you.

I’m not much of a GUI developer.

Never was one, never will be.

I enjoy creating the occasional user interface with HTML + CSS, or hacking up a WordPress plugin to make it more aesthetically pleasing — but writing full-fledged GUIs was never something I enjoyed.

That said, I do try my best to write about what you, the PyImageSearch audience wants to hear. And over the past few months, I’ve received a bunch of emails asking about Python GUI libraries, which ones to use, and more specifically, how to integrate OpenCV with Tkinter to display an image in a Tkinter panel.

I normally respond with:

I’m not a GUI developer, so trust me, you don’t want my advice on this. But if you’re looking to build GUIs with Python, look into Qt, Tkinter, and Kivy (if you want to build mobile/natural user interfaces).

All that said, I eventually received enough emails regarding OpenCV + Tkinter integration that it piqued my interest and I had to give it a try.

Over the next two blog posts, I’ll be playing around with Tkinter, developing some simple user interfaces, and sharing my code/experiences with you. I’ve emphasized playing around here because these are by no means production applications (and more than likely) not great examples of GUI development practices.

Like I said, I don’t pretend to be a GUI developer — I just want to share my experiences with you.

Looking for the source code to this post?
Jump right to the downloads section.

Using OpenCV with Tkinter

In this tutorial, we’ll be building a simple user interface using the Tkinter Python library. This user interface will allow us to click a button, triggering a file chooser dialog to select a file from disk. We’ll then load the selected image using OpenCV, perform edge detection, and finally display both the original image and edge map in our GUI.

I’ve included a rough wireframe of the first mockup below:

Figure 1: Our first screen contains only a button to load our image from disk.

Figure 1: Our first screen contains only a button to load our image from disk.

When first loaded, our application when contain only a button that allows us to load an image from disk.

After clicking this button, we’ll be allowed to navigate our file system and select an image to be processed. We’ll then display the original image and edge map in the GUI.

Figure 2: After selecting an image, we'll display both the original image and edge map in our GUI.

Figure 2: After selecting an image, we’ll display both the original image and edge map side-by-side in our GUI.

What is Tkinter?

If you haven’t heard of Tkinter before, Tkinter is a thin object-oriented layer around Tcl/Tk. One of the benefits of using Tkinter, is that with it installed on your system, you can run any Python GUI applications that leverage Tcl/Tk.

In the very rare occurrences that I’ve needed to develop a GUI application for Python, I tend to use Qt, but since it’s been a good 3-4 years since I’ve last used Tkinter, I thought I would give it another shot.

Installing Tkinter

I personally struggled to get Tkinter installed and configured properly on my OSX machine, so I decided to revert to using Ubuntu/Raspbian.

Note: If you have any good tutorials for installing Tkinter and Tcl/Tk on OSX, please leave the links in the comments section of this blog post.

While I struggled to get Tkinter installed on OSX, installing on Ubuntu/Raspbian was a breeze, requiring only a call to apt-get :

From there, Tkinter was installed without a hitch.

You should also make sure you have Pillow, a simple Python-based imaging library installed as well since Tkinter will need it for displaying images in our GUI:

I validated my installation by firing up a Python shell and importing PIL/Pillow, Tkinter, and my OpenCV bindings:

Note: I’m assuming that you already have OpenCV installed on your system. If you need help configuring, compiling, and installing OpenCV, please see this page where I’ve compiled a list of OpenCV installation instructions for a variety of systems.

Writing our OpenCV + Tkinter application

We are now ready to write our simple GUI application. Open up a new file, name it , and insert the following code:

Lines 2-6 import our required Python packages. We’ll need Tkinter  to access our GUI functionality, along with both the Image  and ImageTk  classes from PIL/Pillow to display the image in our GUI. The tkFileDialog  allows us to browse our file system and choose an image to process. Finally, we import cv2  for our OpenCV bindings.

Line 8 defines our select_image  function. Inside this function, we grab a global reference to panelA  and panelB , respectively. These were be our image panelsAn image panel, as the name suggests, is used to take an image and then display it in our GUI.

The first panel, panelA , is for the original image we will load from disk, while the second panel, panelB , will be for the edge map we are going to compute.

A call to tkFileDialog.askopenfilename  on Line 14 opens a file chooser dialog, which we can use to navigate our file system and select an image of our choosing.

After selecting an image, our program continues:

Line 17 ensures that a file was selected and that we did not click the “Cancel” button. Provided that a path  was selected, we load the image from disk, convert it to grayscale, and then detect edges using the Canny edge detector (Lines 20-22). I’ve hardcoded the thresholds to the Canny edge detector as a matter of simplicity, but you could also use the auto_canny  function from the imutils  package to compute the edge map without supplying any parameters. You can read more about the auto-canny function here.

In order to display our images in the Tkinter GUI, we first need to change the formatting. To start, OpenCV represents images in BGR order; however PIL/Pillow represents images in RGB order, so we need to reverse the ordering of the channels (Line 26).

From there, we can convert image  and edged  from OpenCV format to PIL/Pillow format (Lines 29 and 30). And then finally convert the PIL/Pillow images to ImageTk  format (Lines 33 and 34).

We are now ready to add the images to our GUI:

If the respective panels are None , we need to initialize them (Lines 37-46). First, we create an instance of Label  for each image. Then, we take special care to include panel.image = image .

Why is this so important?

To prevent Python’s garbage collection routines from deleting the image!

Without storing this reference, our image will essentially be deleted and we will be unable to display it to our screen.

Otherwise, we can assume that our image panels have already been initialized (Lines 49-54). In this case, all we need to do is call configure  on each of the panels and then update the reference to the image objects.

The last step is to write the code that actually initializes, creates, and starts the GUI process:

Line 57 initializes the root  Tkinter window, while Lines 58 and 59 initialize our two image panels.

We then create a button and add it to our GUI on Lines 64 and 65. When clicked, this button will trigger our file chooser, allowing us to navigate our file system and select an image from disk.

Finally, Line 68 kicks-off the actual main loop of the GUI.

Running our OpenCV + Tkinter GUI

To run our OpenCV + Tkinter application, just execute the following command:

At first, all our GUI should contain is the button we click to select an image from disk:

Figure 3: At startup, our GUI only contains a single button, that when clicked, will prompt us to select a file from disk.

Figure 3: At startup, our GUI only contains a single button, that when clicked, will prompt us to select a file from disk.

After clicking the button, we are presented with a file chooser:

Figure 4: The file chooser allows us to navigate our file system and select an image to process.

Figure 4: The file chooser allows us to navigate our file system and select an image to process.

We can then navigate to the image file we want to compute edges for and click the “Open” button:

Figure 5: We can select an image to process by first highlighting the image and then clicking the "Open" button.

Figure 5: We can select an image to process by first highlighting the image and then clicking the “Open” button.

After our image has been selected, the edge map is computed using OpenCV, and both the original image and the edge map are added to our Tkinter GUI:

Figure 6: On the left, we have the original, unprocessed image. Then, on the right, we have the edge map.

Figure 6: On the left, we have the original, unprocessed image. Then, on the right, we have the edge map.

Of course, we can repeat this process for different images as well:

Figure 7: Loading an image, converting edges using the Canny edge detector, and then displaying the result using Tkinter and OpenCV.

Figure 7: Loading an image, computing edges using the Canny edge detector, and then displaying the result using Tkinter and OpenCV.

Let’s do one final example:

Figure 8: Displaying images in Tkinter using OpenCV.

Figure 8: Displaying images in Tkinter using OpenCV.


In this blog post, I demonstrated how to build a very simple GUI application that integrates both OpenCV for computer vision and the Tkinter library for developing GUIs with the Python programming language.

I’ll be the first one to say that I am not a GUI developer, nor do I have any intentions of becoming one. This code is likely far from perfect — and that’s okay. I simply wanted to share my experience with you in the hopes that it helps other, more committed and advanced GUI developers learn how to integrate OpenCV with Tkinter.

All that said, next week I’ll be doing a second blog post on OpenCV and Tkinter, this time building a “Photo Booth” application that can access our webcam, display the (live) stream to our GUI, and save snapshots from the stream to disk at the click of a button.

To be notified when this blog post is published, be sure to enter your email address in the form below!


If you would like to download the code and images used in this post, please enter your email address in the form below. Not only will you get a .zip of the code, I’ll also send you a FREE 17-page Resource Guide on Computer Vision, OpenCV, and Deep Learning. Inside you'll find my hand-picked tutorials, books, courses, and libraries to help you master CV and DL! Sound good? If so, enter your email address and I’ll send you the code immediately!

, ,

49 Responses to OpenCV with Tkinter

  1. Gorian May 23, 2016 at 12:17 pm #

    Great!! You are reading on my mind. This morning I thought about connection of OpenCV and Tkinter. I am starting to learn it immediately.

    • Adrian Rosebrock May 23, 2016 at 7:18 pm #

      Fantastic Gorian!

      • Gorian May 24, 2016 at 3:18 pm #

        I had a small problem with Pillow under Ubuntu. I couldn’t import ImageTK. On the Internet I found just a one small, old post, that I need install Pillow 2.5.1 instead the last version. Now everything is working fine, but why under old version?

        • Adrian Rosebrock May 25, 2016 at 3:28 pm #

          I’m honestly not sure, that’s quite strange! I’m not an expert on PIL/Pillow/TKinter though, so that’s likely why 😉

  2. Robin Kinge May 23, 2016 at 2:30 pm #

    Thanks yet again for a simple tutorial. You seem to have a knack of posting these exactly when I need them


    • Adrian Rosebrock May 23, 2016 at 7:18 pm #

      Thanks Robin, I’m glad I could help! 🙂

      • Robin Kinge May 25, 2016 at 11:41 am #

        Finally got to test this and I have to import tkinter (no caps).

        That seems to be ok, but it refuses to find tkFileDialog


        P.S. this lesson does not appear from your web page. I can only find it by clicking on the link in you e-mail.

        • Adrian Rosebrock May 25, 2016 at 3:14 pm #

          The tkFileDialogIssue might be due to your version of TKinter, I’m not sure. As for the post not appearing on the PyImageSearch website, try refreshing. The homepage is likely cached in your web browser.

          • olivier78860 May 26, 2016 at 3:00 am #

            “sudo pip install image” did the trick for me.

  3. leena May 23, 2016 at 11:31 pm #

    Great ! Thanks for this

  4. Ankit May 24, 2016 at 4:29 am #

    Another great post Adrian! Thanks

  5. Linus May 30, 2016 at 11:39 am #

    Hey Adrian,
    another great tutorial! While I personally like GUI programming, I would be happy to see some OpenCV + Qt tutorials here in future! One thing you could fix: In the section Writing our OpenCV + Tkinter application, there’s *Lien8* insted of *Line8* 🙂

    • Adrian Rosebrock May 31, 2016 at 3:51 pm #

      Thanks for letting me know Linus! 🙂

  6. Leland Green May 30, 2016 at 11:43 am #

    I’m going to play with this one soon. You are posting some really cool stuff. Keep up the great work!

  7. Mickey June 14, 2016 at 11:39 am #

    Thank you, Adrian! You are the best

    • Adrian Rosebrock June 15, 2016 at 12:35 pm #

      No problem Mickey, I’m happy I could help 🙂

  8. Arasch U Lagies June 18, 2016 at 2:03 am #

    Hi Adrian,
    By mistake I installed the Tkinter packages and the Pillow outside of the cv virtual environment. Now Tkinter imports only outside the the virtual environment and cv2 only inside. I also tried removing the packages and installing everything in the virtual environment. But I still get this error.
    Any idea what the problem could be?

    • Adrian Rosebrock June 18, 2016 at 8:11 am #

      This might be a bit of a pain to debug, but I would install OpenCV outside the virtual environment as well. Assuming your system Python and virtual environment Python are the same version, just copy the files from your virtual environment site-packages directory to the site-packages directory of your system Python.

    • Scott November 22, 2016 at 4:48 pm #

      I made the same mistake and couldn’t resolve it until I ran across a post that suggested upgrading pip (pip3, in my case). Before I upgraded pip, when I tried to install Pillow in the virtual environment I would get “Requirement already satisfied” because of the installation outside of the virtual environment. After I upgraded pip and reinstalled pip3, I was able to install Pillow in the virtual environment without problem.

  9. Oscar Morales July 24, 2016 at 3:21 am #

    Great ! Thanks for this

  10. Scott November 5, 2016 at 12:06 am #

    Hi Adrian,

    Thanks for another great post! I ran into the same issue with tkFileDialog. It looks like tkFileDialog was renamed filedialog in the Python3 version of tkinter (also, tkinter has no caps in the Python3 version for some reason). If others are running into this problem, it might be a Python3 vs Python2 issue. It can be fixed with a few minor edits for Python3…

    Line 2 becomes… from tkinter import * (no caps)
    Line 5 becomes… import tkinter.filedialog
    Line 14 becomes … path = tkinter.filedialog.askopenfilename()

    • Adrian Rosebrock November 7, 2016 at 2:54 pm #

      Thanks for sharing Scott!

  11. Tobil June 8, 2017 at 3:40 am #

    great tutorial Adrian, thanks for sharing…
    it’s interresting to learn how to combine opencv and windows GUI using python code.

  12. Jose Luis Berzal July 12, 2017 at 1:02 pm #

    For installation on Mac, I followed this tutorial:

    And, things went smooth, apparently the key is to choose the right package for mac.

    • Adrian Rosebrock July 12, 2017 at 2:38 pm #

      Thanks for sharing Jose!

  13. Dan March 3, 2018 at 7:47 pm #

    I’m amazed that any of this works. When I use opencv with askopenfilenames I am only able to select a file name once. The 2nd time I click on a file tkinter shuts the main window down and returns me to the python (2.7.7) shell without any error messages. Switching to imagetk.photoimage to try to show a series of images solved this problem but created another, as only the last image is displayed. Using a reference as suggested above doesn’t seem to work.

    • Adrian Rosebrock March 7, 2018 at 9:41 am #

      I wish I was a GUI expert but I’m unfortunately not. I hope other TKinter users can comment and help out on this thread.

  14. babul March 12, 2018 at 7:17 am #

    if panelA is None or panelB is None:
    NameError: global name ‘panelA’ is not defined
    this is my error.I can not understand what’s the problem

    • Adrian Rosebrock March 14, 2018 at 1:06 pm #

      Make sure you download the source code to this post using the “Downloads” section rather than copying and pasting. This will avoid most errors.

  15. alina May 26, 2018 at 9:51 am #

    Hey Adrian,

    Is possible to make the graphical interface with tkinter ie to create several buttons each button to make a simulation for example button to make the distance between object and button to make the image classification and the other to make the difference between two picture

    thanks in advance

    • Adrian Rosebrock May 28, 2018 at 9:49 am #

      Sure, absolutely. But you would need to code your system to run the proper routine on each button press.

  16. alina May 28, 2018 at 11:56 am #

    Hey Adrian,

    I did the GUI but there is a problem at the image classification program level (neural network), I write “python3” in the terminal but it displays the following problem:
    usage: [-h] -m MODEL -l LABELBIN -i IMAGE error: the following arguments are required: -m / – model, -l / – labelbin, -i / – image
    and when I write python3 –model pokedex.model –labelbin lb.pickle – picture examples / resistor_4.jpg, the other programs (distance between object, difference between two picture) do not work

    thanks in advanc

  17. Guido October 10, 2018 at 12:56 pm #

    Hello Adrian,
    what if I need to load the image using tkinter and then convert into an array for OpenCV. So the other way around.. how would you suggest to do it?

    • Adrian Rosebrock October 12, 2018 at 9:13 am #

      I would start by looking into what function Tkinter is using to load the image. Typically it will be either PIL or NumPy.

  18. Kony Kim December 19, 2018 at 2:27 am #

    Hi! Do you know how to resize the displayed image?

    • Adrian Rosebrock December 19, 2018 at 1:49 pm #

      You could use either the “cv2.resize” or “imutils.resize” functions to resize the frame.

      • Kony Kim December 19, 2018 at 10:51 pm #

        I tried to use it but only the frame was changed not the image inside the frame

  19. Shreya March 11, 2019 at 7:27 am #

    How can i download Tkinter on windows and on my python environment?

  20. sampreeti March 25, 2019 at 1:27 am #

    how to build user interface for object detection?

    • Adrian Rosebrock March 27, 2019 at 8:57 am #

      What specifically should the user interface do?

  21. Ishwar Bhoge April 2, 2019 at 2:19 am #

    Hello Adrian,
    What should i do to show all images in filedialog in thumbnails view except list.


  22. Bill Strum April 12, 2019 at 4:45 pm #

    How can I display (convert) a cv2 histogram or other graph to a tk image for display in a a GUI?

  23. jorge anza May 31, 2019 at 1:34 am #

    from tkinter import Tk, Label, filedialog
    from PIL import Image
    from PIL import ImageTk

    there are the correct imports

  24. Eman June 17, 2019 at 7:48 am #

    How can we get the mouse click position on the two images ?

  25. rebecca July 19, 2019 at 12:08 pm #

    Is there anyway of displaying an image in grayscale in Tkinter? I have converted the image to grayscale using cv2 but it then doesn’t display as such

  26. Yonten Jamtsho August 27, 2019 at 10:42 pm #

    Instead of Tkinter, can we use PyQt5 for development of OpenCV GUI?


  1. Displaying a video feed with OpenCV and Tkinter - PyImageSearch - May 30, 2016

    […] said it in last week’s blog post and I’ll say it again here today — I am not, by any stretch of the imagination, a GUI […]

Before you leave a comment...

Hey, Adrian here, author of the PyImageSearch blog. I'd love to hear from you, but before you submit a comment, please follow these guidelines:

  1. If you have a question, read the comments first. You should also search this page (i.e., ctrl + f) for keywords related to your question. It's likely that I have already addressed your question in the comments.
  2. If you are copying and pasting code/terminal output, please don't. Reviewing another programmers’ code is a very time consuming and tedious task, and due to the volume of emails and contact requests I receive, I simply cannot do it.
  3. Be respectful of the space. I put a lot of my own personal time into creating these free weekly tutorials. On average, each tutorial takes me 15-20 hours to put together. I love offering these guides to you and I take pride in the content I create. Therefore, I will not approve comments that include large code blocks/terminal output as it destroys the formatting of the page. Kindly be respectful of this space.
  4. Be patient. I receive 200+ comments and emails per day. Due to spam, and my desire to personally answer as many questions as I can, I hand moderate all new comments (typically once per week). I try to answer as many questions as I can, but I'm only one person. Please don't be offended if I cannot get to your question
  5. Do you need priority support? Consider purchasing one of my books and courses. I place customer questions and emails in a separate, special priority queue and answer them first. If you are a customer of mine you will receive a guaranteed response from me. If there's any time left over, I focus on the community at large and attempt to answer as many of those questions as I possibly can.

Thank you for keeping these guidelines in mind before submitting your comment.

Leave a Reply