showing video on the entire screen using OpenCV and Tkiner
It's not a hard task if you don't care about execution time! We knew that resizing of an image isn't a rocket science for common user, but under the hood it takes some time to resize each frame. And if you really wonder about time and options - there're many options to play around from numpy
/scipy
to skimage
/skvideo
.
But let's try to do something with your code "as is" so we have two options to play with: cv2
and Image
. For testing I grabbed 20 secs of "Keyboard Cat" video from youtube (480p) and resize each frame upto 1080p, and GUI looks like this (fullscreen 1920x1080):
Resize Methods / timeit
elapsed time of showing frames:
cv2.resize()
/ ~81.377 s.Image.resize()
/ ~82.98 s.
As you see - no big difference between theese two so here's a code (only Application
class and video_loop
changed):
#importstry: import tkinter as tkexcept: import Tkinter as tkfrom PIL import Image, ImageTkimport argparseimport datetimeimport cv2import osclass Application: def __init__(self, output_path = "./"): """ Initialize application which uses OpenCV + Tkinter. It displays a video stream in a Tkinter window and stores current snapshot on disk """ self.vs = cv2.VideoCapture('KeyCat.mp4') # capture video frames, 0 is your default video camera self.output_path = output_path # store output path self.current_image = None # current image from the camera self.root = tk.Tk() # initialize root window self.root.title("PyImageSearch PhotoBooth") # set window title # self.destructor function gets fired when the window is closed self.root.protocol('WM_DELETE_WINDOW', self.destructor) self.root.attributes("-fullscreen", True) # getting size to resize! 30 - space for button self.size = (self.root.winfo_screenwidth(), self.root.winfo_screenheight() - 30) self.panel = tk.Label(self.root) # initialize image panel self.panel.pack(fill='both', expand=True) # create a button, that when pressed, will take the current frame and save it to file self.btn = tk.Button(self.root, text="Snapshot!", command=self.take_snapshot) self.btn.pack(fill='x', expand=True) # start a self.video_loop that constantly pools the video sensor # for the most recently read frame self.video_loop() def video_loop(self): """ Get frame from the video stream and show it in Tkinter """ ok, frame = self.vs.read() # read frame from video stream if ok: # frame captured without any errors cv2image = cv2.cvtColor(frame, cv2.COLOR_BGR2RGBA) # convert colors from BGR to RGBA cv2image = cv2.resize(cv2image, self.size, interpolation=cv2.INTER_NEAREST) self.current_image = Image.fromarray(cv2image) #.resize(self.size, resample=Image.NEAREST) # convert image for PIL self.panel.imgtk = ImageTk.PhotoImage(image=self.current_image) self.panel.config(image=self.panel.imgtk) # show the image self.root.after(1, self.video_loop) # call the same function after 30 milliseconds
But you knew - do such a things "on fly" isn't a good idea, so lets try to resize all frames first and then do all stuff(only Application
class and video_loop
method changed, resize_video
method added):
class Application: def __init__(self, output_path = "./"): """ Initialize application which uses OpenCV + Tkinter. It displays a video stream in a Tkinter window and stores current snapshot on disk """ self.vs = cv2.VideoCapture('KeyCat.mp4') # capture video frames, 0 is your default video camera ... # init frames self.frames = self.resize_video() self.video_loop()def resize_video(self): temp = list() try: temp_count_const = cv2.CAP_PROP_FRAME_COUNT except AttributeError: temp_count_const = cv2.cv.CV_CAP_PROP_FRAME_COUNT frames_count = self.vs.get(temp_count_const) while self.vs.isOpened(): ok, frame = self.vs.read() # read frame from video stream if ok: # frame captured without any errors cv2image = cv2.cvtColor(frame, cv2.COLOR_BGR2RGBA) # convert colors from BGR to RGBA cv2image = cv2.resize(cv2image, self.size, interpolation=cv2.INTER_NEAREST) cv2image = Image.fromarray(cv2image) # convert image for PIL temp.append(cv2image) # simple progress print w/o sys import print('%d/%d\t%d%%' % (len(temp), frames_count, ((len(temp)/frames_count)*100))) else: return tempdef video_loop(self): """ Get frame from the video stream and show it in Tkinter """ if len(self.frames) != 0: self.current_image = self.frames.pop(0) self.panel.imgtk = ImageTk.PhotoImage(self.current_image) self.panel.config(image=self.panel.imgtk) self.root.after(1, self.video_loop) # call the same function after 30 milliseconds
timeit
elapsed time of showing pre-resized frames: ~78.78 s.
As you see - resizing isn't a main problem of your script, but a good option!