0

Is it possible to display a window which has been made by OpenCV using Tkinter? I want to open it using Tkinter so that I can provide more GUI functions. Has this been done before? I checked google and SO itself but did not find anything.

So as kobejohn suggested, I am attaching the code for the camera capture and display.

import cv2
import urllib 
import numpy as np
import subprocess

stream=urllib.urlopen('IP Address')
bytes=''
while True:
    bytes+=stream.read(1024)
    a = bytes.find('\xff\xd8')
    b = bytes.find('\xff\xd9')
    if a!=-1 and b!=-1:
        jpg = bytes[a:b+2]
        bytes= bytes[b+2:]
        i = cv2.imdecode(np.fromstring(jpg, dtype=np.uint8),cv2.CV_LOAD_IMAGE_COLOR)
        cv2.imshow('i',i)
        if cv2.waitKey(1) ==27:
            exit(0)
praxmon
  • 5,009
  • 22
  • 74
  • 121
  • To answer directly, I don't know how and I would not try it unless I had a good reason to avoid other solutions. Could you convert the image data and put it into tkinter directly? I think your development time will be much lower. – KobeJohn Feb 24 '14 at 05:05
  • @kobejohn the thing is I am not dealing with a video, I am dealing with an mjpeg stream. I don't know how to access it using tkinter. If I consider the images that I am getting and converting them and then displaying them in Tkinter, then how do I go about it? And if I don't use tkinter, can I use something else to build a GUI which will support this? Something like wxwidgets, pygtk or qt? – praxmon Feb 24 '14 at 05:34
  • You can build OpenCV with Qt as GUI backend (when you compile opencv from source, there is a WITH_QT option). Then you will be able to build more complex GUI with Qt – remi Feb 24 '14 at 11:03
  • @remi On windows. Do you know how to do that on windows? – praxmon Feb 24 '14 at 11:15
  • @PrakharMohanSrivastava can you post a very minimal piece of code for displaying video in opencv? Then someone can show you how to convert the video/images to tkinter format. – KobeJohn Feb 24 '14 at 12:33
  • @PrakharMohanSrivastava On Windows? You mean microsoft windows? You QT and OpenCV works on windows, you still have to compile opencv on windows though, unless you find a precompiled version for windows with Qt. Also, follow kobejohn's advice to make sure we understand your question – remi Feb 25 '14 at 09:12
  • @remi I did add the code like kobejohn said and I tried embedding the OpenCV windows in a Qt app, sadly it didn't work. I asked a lot and googled a lot but sadly could not find a solution so any help will be appreciated. – praxmon Feb 25 '14 at 09:17
  • @kobejohn I added the code. Please check. You understand the question? – praxmon Feb 25 '14 at 09:18

1 Answers1

3

This code is based on the discussion in comments. It doesn't put the opencv window into tkinter. It just takes opencv images and puts them into tkinter.

Prakhar, I don't have an available IP camera so can you try this? I have confirmed that it works with the USB code at the bottom of this answer.

Basically, I just inserted your jpg reading code into a simplified version of this SO question to get the code below. It uses a 2-step conversion: bytes --> opencv image --> tkinter image. There may be a more efficient way to convert directly from the bytes to a tkinter image but you can fix that if performance becomes a problem.


IP Camera

import cv2
import numpy as np
import PIL.Image
import PIL.ImageTk
import Tkinter as tk
import urllib

stream = urllib.urlopen('IP Address')
bytes_ = ''


def update_image(image_label):
    global bytes_
    bytes_ += stream.read(1024)
    a = bytes_.find('\xff\xd8')
    b = bytes_.find('\xff\xd9')
    if (a != -1) and (b != -1):
        jpg = bytes_[a:b+2]
        bytes_ = bytes_[b+2:]
        cv_image = cv2.imdecode(np.fromstring(jpg, dtype=np.uint8),
                                cv2.CV_LOAD_IMAGE_COLOR)
        cv_image = cv2.cvtColor(cv_image, cv2.COLOR_BGR2RGB)
        pil_image = PIL.Image.fromarray(cv_image)
        tk_image = PIL.ImageTk.PhotoImage(image=pil_image)
        image_label.configure(image=tk_image)
        image_label._image_cache = tk_image  # avoid garbage collection
        root.update()


def update_all(root, image_label):
    if root.quit_flag:
        root.destroy()  # this avoids the update event being in limbo
    else:
        update_image(image_label)
        root.after(1, func=lambda: update_all(root, image_label))


if __name__ == '__main__':
    root = tk.Tk()
    setattr(root, 'quit_flag', False)
    def set_quit_flag():
        root.quit_flag = True
    root.protocol('WM_DELETE_WINDOW', set_quit_flag)
    image_label = tk.Label(master=root)  # label for the video frame
    image_label.pack()
    root.after(0, func=lambda: update_all(root, image_label))
    root.mainloop()

USB Camera

*edit - I have confirmed that the code below works to take video from a USB camera using opencv and send it to a tkinter window. So hopefully the above code will work for your ip camera.

import cv2
import PIL.Image
import PIL.ImageTk
import Tkinter as tk


def update_image(image_label, cv_capture):
    cv_image = cv_capture.read()[1]
    cv_image = cv2.cvtColor(cv_image, cv2.COLOR_BGR2RGB)
    pil_image = PIL.Image.fromarray(cv_image)
    tk_image = PIL.ImageTk.PhotoImage(image=pil_image)
    image_label.configure(image=tk_image)
    image_label._image_cache = tk_image  # avoid garbage collection
    root.update()


def update_all(root, image_label, cv_capture):
    if root.quit_flag:
        root.destroy()  # this avoids the update event being in limbo
    else:
        update_image(image_label, cv_capture)
        root.after(10, func=lambda: update_all(root, image_label, cv_capture))


if __name__ == '__main__':
    cv_capture = cv2.VideoCapture()
    cv_capture.open(0)  # have to use whatever your camera id actually is
    root = tk.Tk()
    setattr(root, 'quit_flag', False)
    def set_quit_flag():
        root.quit_flag = True
    root.protocol('WM_DELETE_WINDOW', set_quit_flag)  # avoid errors on exit
    image_label = tk.Label(master=root)  # the video will go here
    image_label.pack()
    root.after(0, func=lambda: update_all(root, image_label, cv_capture))
    root.mainloop()
Community
  • 1
  • 1
KobeJohn
  • 7,390
  • 6
  • 41
  • 62
  • Thank you! It works but it is lagging heavily. Any reason why? – praxmon Feb 26 '14 at 03:52
  • You would need to add some timing or use profiling to find out what is taking time. My USB setup works ok with a reasonable frame rate (maybe 15~20?) so I guess the byte reading code is slow or that you are using high resolution images? Does it work quickly with `cv2.imshow(...)`? PIL(or PILLOW) is the standard basic image library for python. `PIL.ImageTk.PhotoImage` is a special object made to work with tkinter. In tkinter, simple images are just pasted onto labels so we paste the PhotoImage onto `image_label`. `_image_cache` is unfortunately required to avoid garbage collection. – KobeJohn Feb 26 '14 at 06:40
  • I figured it out. The update_all function is calling the update_image function after a gap of 10 seconds/milliseconds. I reduced that time to 0, now it works fine. I hope I am not doing anything wrong which will spoil the code. Also, what do I do if I have to display a canvas in the same window? Just a basic canvas apart from the label? – praxmon Feb 26 '14 at 06:42
  • @PrakharMohanSrivastava Oh right. Sorry about putting the large delay. I will remove it so it doesn't cause problems for anyone else. I'm glad it works! For other tkinter work, you should definitely try a tkinter tutorial to see what you can do. You could put it in the same window or you could put it in a different window. – KobeJohn Feb 26 '14 at 06:46